/*
 * (C) Copyright 2001
 * Denis Peter, MPL AG Switzerland, d.peter@mpl.ch
 *
 * 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
 *
 * hacked for PIP405
 */

#include <ppcboot.h>
#include <command.h>
#include "pip405.h"
#include <rtc.h>
#include "pip405_isa.h"
#include "video.h"
#include <i2c.h>

extern flash_info_t flash_info[];	/* info for FLASH chips */

int PCI_Write_CFG_Reg(int BusDevFunc, int Reg, unsigned int Value, int Width);
unsigned int PCI_Read_CFG_Reg(int BusDevFunc, int Reg, int Width);


#define TRUE 1
#define FALSE 0

#define IMAGE_SIZE 0x80000 

image_header_t header;



int pip405_prg(unsigned long src,unsigned long size)
{
	unsigned long start;
	flash_info_t *info;
	int i,rc;
	unsigned long *magic = (unsigned long *)src;
	
	info = &flash_info[0];
  start = 0 - size;
	for(i=info->sector_count-1;i>0;i--)
	{
		info->protect[i] = 0; /* unprotect this sector */
		if(start>=info->start[i])
		break;
	}
	/* set-up flash location */
	rc=switch_cs(FALSE);
	/* now erase flash */
	if(magic[0]!=IH_MAGIC) {
		printf("Bad Magic number\n");
		switch_cs(rc);
		return(0);
	}
	printf("Start erasing at %lx (sector %d) (start %lx)\n",
				start,i,info->start[i]);
	flash_erase (info, i, info->sector_count-1);
	printf("flash erased, programming 0x%lx Bytes\n",size);
	switch (rc = flash_write ((uchar *)src, start, size)) 
	{
		case 0:	printf ("programming done\n"); return(1);
		case 1: printf ("Timeout writing to Flash\n"); return(0);
		case 2:	printf ("Flash not Erased\n"); return(0);
		case 4: printf ("Can't write to protected Flash sectors\n"); return(0);
		case 8: printf ("Outside available Flash\n"); return(0);
		default:	printf ("%s[%d] FIXME: rc=%d\n",__FILE__,__LINE__,rc); return(0);
	}
  return(0);
}

int pip405_prg_image(unsigned long load_addr)
{
	unsigned long data,len,checksum;
	image_header_t *hdr=&header;  
	/* Copy header so we can blank CRC field for re-calculation */
	memcpy (&header, (char *)load_addr, sizeof(image_header_t));

	if (hdr->ih_magic  != IH_MAGIC) {
		printf ("Bad Magic Number\n");
		return(0);
	}
	if (hdr->ih_os  != IH_OS_PPCBOOT) {
		printf ("No PPCBOOT Image\n");
		return(0);
	}
	if (hdr->ih_type  != IH_TYPE_FIRMWARE) {
		printf ("No Firmware Image\n");
		return(0);
	}
	data = (ulong)&header;
	len  = sizeof(image_header_t);
	checksum = hdr->ih_hcrc;
	hdr->ih_hcrc = 0;
	if (crc32 (0, (char *)data, len) != checksum) {
		printf ("Bad Header Checksum\n");
		return(0);
	}
	data = load_addr + sizeof(image_header_t);
	len  = hdr->ih_size;
	printf ("   Verifying Checksum ... ");
	if (crc32 (0, (char *)data, len) != hdr->ih_dcrc) {
		printf ("Bad Data CRC\n");
		return(0);
	}
	printf ("OK\n");
	return(pip405_prg(data,len));
} 


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


void do_pip405(cmd_tbl_t *cmdtp, bd_t *bd, int flag, int argc, char *argv[])
{

    ulong size,src,device,function,load_addr;
    unsigned char i2c[3];
		
		src = MULTI_PURPOSE_SOCKET_ADDR;
		size = IMAGE_SIZE;

		if (strcmp(argv[1], "flash") == 0) 
		{
		
			if (strcmp(argv[2], "floppy") == 0) {
 				char *local_args[3];
				extern void do_fdcboot (cmd_tbl_t *, bd_t *, int, int, char *[]);
				printf ("\nupdating bootloader image from floppy\n");
				local_args[0] = argv[0];
	    	if(argc==4) {
					local_args[1] = argv[3];
					local_args[2] = NULL;
					load_addr=simple_strtoul(argv[3], NULL, 16);
					do_fdcboot(cmdtp, bd, 0, 2, local_args);
				}
				else {
					local_args[1] = NULL;
					load_addr=CFG_LOAD_ADDR;
					do_fdcboot(cmdtp, bd, 0, 1, local_args);
				}
				pip405_prg_image(load_addr); 
				return;
			}
			if (strcmp(argv[2], "mem") == 0) {
	    	if(argc==4) {
					load_addr=simple_strtoul(argv[3], NULL, 16);
				}
				else {
					load_addr=CFG_LOAD_ADDR;
				}
				printf ("\nupdating bootloader image from memory at %lX\n",load_addr);
				pip405_prg_image(load_addr); 
				return;
			}
			if (strcmp(argv[2], "mps") == 0) {
				printf ("\nupdating bootloader image from MSP\n");
				pip405_prg(src,size);
				return;
			}
		}
		if (strcmp(argv[1], "prg") == 0) 
		{
	    if(argc==3)
	    {
		    size = (ulong)simple_strtoul(argv[2], NULL, 16);
		    printf("image size is 0x%lx\n",size);
	    }
			pip405_prg(src,size);
			return;
		}
 		if (strcmp(argv[1], "info") == 0) 
		{
	    printf("argc %d\n",argc);
			print_pip405_info();
		 	return;
   	}
 		if (strcmp(argv[1], "i2c") == 0) 
		{
	    printf("argc %d\n",argc);
	    i2c[0]=CFG_I2C_EEPROM_ADDR;
	    i2c[1]=0;
	    i2c[2]=0;
	    i2c_read(&i2c[0],3,(unsigned char *)CFG_LOAD_ADDR,512);
			return;
   	}
 		if (strcmp(argv[1], "led") == 0) 
		{
	    printf("argc %d\n",argc);
	    device = (ulong)simple_strtoul(argv[2], NULL, 10);
	    function = (ulong)simple_strtoul(argv[3], NULL, 10);
			if(device==0)
				user_led0((function==1));
			else
				user_led1((function==1));
			return;
   	}
    printf("Usage:\n%s\n", cmdtp->usage);
    return;
}

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