/********************************************************************
 *
 * Unless otherwise specified, Copyright (C) 2004-2005 Barco Control Rooms
 *
 * $Source: /home/services/cvs/firmware/ppc/u-boot-1.1.2/board/barco/barco.c,v $
 * $Revision: 1.4 $
 * $Author: mleeman $
 * $Date: 2005/03/02 16:40:20 $
 *
 * Last ChangeLog Entry
 * $Log: barco.c,v $
 * Revision 1.4  2005/03/02 16:40:20  mleeman
 * remove empty labels (3.4 complains)
 *
 * Revision 1.3  2005/02/21 12:48:58  mleeman
 * update of copyright years (feedback wd)
 *
 * Revision 1.2  2005/02/21 10:10:53  mleeman
 * - split up switch statement to a function call (Linux kernel coding guidelines)
 *   ( feedback wd)
 *
 * Revision 1.1  2005/02/14 09:31:07  mleeman
 * renaming of files
 *
 * Revision 1.1  2005/02/14 09:23:46  mleeman
 * - moved 'barcohydra' directory to a more generic barco; since we will be
 *   supporting and adding multiple boards
 *
 * Revision 1.3  2005/02/10 13:57:32  mleeman
 * fixed flash corruption: I should exit from the moment I find the correct value
 *
 * Revision 1.2  2005/02/09 12:56:23  mleeman
 * add generic header to track changes in sources
 *
 *
 *******************************************************************/

/*
 * (C) Copyright 2004
 * Marc Leeman <marc.leeman@barco.com>
 *
 * 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 <common.h>
#include <mpc824x.h>
#include <pci.h>
#include <malloc.h>
#include <command.h>

#include "config.h"
#include "barco_svc.h"

#define TRY_WORKING  (3)
#define BOOT_DEFAULT (2)
#define BOOT_WORKING (1)

int checkboard (void)
{
	/*TODO: Check processor type */

	puts (	"Board: Streaming Video Card for Hydra systems "
#ifdef CONFIG_MPC8240
		"8240"
#endif
#ifdef CONFIG_MPC8245
		"8245"
#endif
		" Unity ##Test not implemented yet##\n");
	return 0;
}

long int initdram (int board_type)
{
	long size;
	long new_bank0_end;
	long mear1;
	long emear1;

	size = get_ram_size (CFG_SDRAM_BASE, CFG_MAX_RAM_SIZE);

	new_bank0_end = size - 1;
	mear1 = mpc824x_mpc107_getreg (MEAR1);
	emear1 = mpc824x_mpc107_getreg (EMEAR1);
	mear1 = (mear1  & 0xFFFFFF00) |
		((new_bank0_end & MICR_ADDR_MASK) >> MICR_ADDR_SHIFT);
	emear1 = (emear1 & 0xFFFFFF00) |
		((new_bank0_end & MICR_ADDR_MASK) >> MICR_EADDR_SHIFT);
	mpc824x_mpc107_setreg (MEAR1, mear1);
	mpc824x_mpc107_setreg (EMEAR1, emear1);

	return (size);
}

/*
 * Initialize PCI Devices, report devices found.
 */
#ifndef CONFIG_PCI_PNP
static struct pci_config_table pci_barcohydra_config_table[] = {
	{ PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, 0x0f, PCI_ANY_ID,
	  pci_cfgfunc_config_device, { PCI_ENET0_IOADDR,
				       PCI_ENET0_MEMADDR,
				       PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER } },
	{ PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, 0x10, PCI_ANY_ID,
	  pci_cfgfunc_config_device, { PCI_ENET1_IOADDR,
				       PCI_ENET1_MEMADDR,
				       PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER } },
	{ }
};
#endif

struct pci_controller hose = {
#ifndef CONFIG_PCI_PNP
	config_table: pci_barcohydra_config_table,
#endif
};

void pci_init_board (void)
{
	pci_mpc824x_init (&hose);
}

int write_flash (char *addr, char value)
{
	char *adr = (char *)0xFF800000;
	int cnt = 0;
	char status,oldstatus;

	*(adr+0x55) = 0xAA; udelay (1);
	*(adr+0xAA) = 0x55; udelay (1);
	*(adr+0x55) = 0xA0; udelay (1);
	*addr = value;

	status = *addr;
	do {
		oldstatus = status;
		status = *addr;

		if ((oldstatus & 0x40) == (status & 0x40)) {
			return 4;
		}
		cnt++;
		if (cnt > 10000) {
			return 2;
		}
	} while ( (status & 0x20) == 0 );

	oldstatus = *addr;
	status = *addr;

	if ((oldstatus & 0x40) == (status & 0x40)) {
		return 0;
	} else {
		*(adr+0x55) = 0xF0;
		return 1;
	}
}

unsigned update_flash (unsigned char *buf)
{
	switch ((*buf) & 0x3) {
	case TRY_WORKING:
		printf ("found 3 and converted it to 2\n");
		write_flash ((char *)buf, (*buf) & 0xFE);
		*((unsigned char *)0xFF800000) = 0xF0;
		udelay (100);
		printf ("buf [%#010x] %#010x\n", buf, (*buf));
		/* XXX - fall through??? */
	case BOOT_WORKING :
		return BOOT_WORKING;
	}
	return BOOT_DEFAULT;
}

unsigned scan_flash (void)
{
	char section[] =  "kernel";
	int cfgFileLen  =  (CFG_FLASH_ERASE_SECTOR_LENGTH >> 1);
	int sectionPtr  = 0;
	int foundItem   = 0; /* 0: None, 1: section found, 2: "=" found */
	int bufPtr;
	unsigned char *buf;

	buf = (unsigned char*)(CFG_FLASH_RANGE_BASE + CFG_FLASH_RANGE_SIZE \
			- CFG_FLASH_ERASE_SECTOR_LENGTH);
	for (bufPtr = 0; bufPtr < cfgFileLen; ++bufPtr) {
		if ((buf[bufPtr]==0xFF) && (*(int*)(buf+bufPtr)==0xFFFFFFFF)) {
			return BOOT_DEFAULT;
		}
		/* This is the scanning loop, we try to find a particular
		 * quoted value
		 */
		switch (foundItem) {
		case 0:
			if ((section[sectionPtr] == 0)) {
				++foundItem;
			} else if (buf[bufPtr] == section[sectionPtr]) {
				++sectionPtr;
			} else {
				sectionPtr = 0;
			}
			break;
		case 1:
			++foundItem;
			break;
		case 2:
			++foundItem;
			break;
		case 3:
		default:
			return update_flash (&buf[bufPtr - 1]);
		}
	}

	printf ("Failed to read %s\n",section);
	return BOOT_DEFAULT;
}

TSBootInfo* find_boot_info (void)
{
	unsigned bootimage = scan_flash ();
	TSBootInfo* info = (TSBootInfo*)malloc (sizeof(TSBootInfo));

	switch (bootimage) {
	case TRY_WORKING:
		info->address = CFG_WORKING_KERNEL_ADDRESS;
		break;
	case BOOT_WORKING :
		info->address = CFG_WORKING_KERNEL_ADDRESS;
		break;
	case BOOT_DEFAULT:
	default:
		info->address= CFG_DEFAULT_KERNEL_ADDRESS;

	}
	info->size = *((unsigned int *)(info->address ));

	return info;
}

void barcobcd_boot (void)
{
	TSBootInfo* start;
	char *bootm_args[2];
	char *buf;
	int cnt;
	extern int do_bootm (cmd_tbl_t *, int, int, char *[]);

	buf = (char *)(0x00800000);
	/* make certain there are enough chars to print the command line here!
	 */
	bootm_args[0] = (char *)malloc (16*sizeof(char));
	bootm_args[1] = (char *)malloc (16*sizeof(char));

	start = find_boot_info ();

	printf ("Booting kernel at address %#10x with size %#10x\n",
			start->address, start->size);

	/* give length of the kernel image to bootm */
	sprintf (bootm_args[0],"%x",start->size);
	/* give address of the kernel image to bootm */
	sprintf (bootm_args[1],"%x",buf);

	printf ("flash address: %#10x\n",start->address+8);
	printf ("buf address: %#10x\n",buf);

	/* aha, we reserve 8 bytes here... */
	for (cnt = 0; cnt < start->size ; cnt++) {
		buf[cnt] = ((char *)start->address)[cnt+8];
	}

	/* initialise RAM memory */
	*((unsigned int *)0xFEC00000) = 0x00141A98;
	do_bootm (NULL,0,2,bootm_args);
}

int barcobcd_boot_image (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
#if 0
	if (argc > 1) {
		printf ("Usage:\n (%d) %s\n", argc, cmdtp->usage);
		return 1;
	}
#endif
	barcobcd_boot ();

	return 0;
}

/* Currently, boot_working and boot_default are the same command. This is
 * left in here to see what we'll do in the future */

U_BOOT_CMD (
		try_working, 1, 1, barcobcd_boot_image,
		" try_working - check flash value and boot the appropriate image\n",
		"\n"
	  );

U_BOOT_CMD (
		boot_working, 1, 1, barcobcd_boot_image,
		" boot_working - check flash value and boot the appropriate image\n",
		"\n"
	  );

U_BOOT_CMD (
		boot_default, 1, 1, barcobcd_boot_image,
		" boot_default - check flash value and boot the appropriate image\n",
		"\n"
	  );
/*
 * We are not using serial communication, so just provide empty functions
 */
int serial_init (void)
{
	return 0;
}
void serial_setbrg (void)
{
	return;
}
void serial_putc (const char c)
{
	return;
}
void serial_puts (const char *c)
{
	return;
}
void serial_addr (unsigned int i)
{
	return;
}
int serial_getc (void)
{
	return 0;
}
int serial_tstc (void)
{
	return 0;
}

unsigned long post_word_load (void)
{
	return 0l;
}
void post_word_store (unsigned long val)
{
	return;
}