/* 
 * Copyright (C) 2000 David J. Mckay (david.mckay@st.com)
 *
 * May be copied or modified under the terms of the GNU General Public
 * License.  See linux/COPYING for more information.                            
 *
 * This file handles programming up the Altera Flex10K that interfaces to
 * the Galileo, and does the PS/2 keyboard and mouse
 *
 */


#include <linux/kernel.h>
#include <linux/smp.h>
#include <linux/smp_lock.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/pci.h>
#include <linux/delay.h>


#include <asm/overdriver/gt64111.h>
#include <asm/overdrive/overdrive.h>
#include <asm/overdrive/fpga.h>

#define FPGA_NotConfigHigh()  (*FPGA_ControlReg) = (*FPGA_ControlReg) | ENABLE_FPGA_BIT
#define FPGA_NotConfigLow()   (*FPGA_ControlReg) = (*FPGA_ControlReg) & RESET_FPGA_MASK

/* I need to find out what (if any) the real delay factor here is */
/* The delay is definately not critical */
#define long_delay() {int i;for(i=0;i<10000;i++);}
#define short_delay() {int i;for(i=0;i<100;i++);}

static void __init program_overdrive_fpga(const unsigned char *fpgacode,
					  int size)
{
	int timeout = 0;
	int i, j;
	unsigned char b;
	static volatile unsigned char *FPGA_ControlReg =
	    (volatile unsigned char *) (OVERDRIVE_CTRL);
	static volatile unsigned char *FPGA_ProgramReg =
	    (volatile unsigned char *) (FPGA_DCLK_ADDRESS);

	printk("FPGA:  Commencing FPGA Programming\n");

	/* The PCI reset but MUST be low when programming the FPGA !!! */
	b = (*FPGA_ControlReg) & RESET_PCI_MASK;

	(*FPGA_ControlReg) = b;

	/* Prepare FPGA to program */

	FPGA_NotConfigHigh();
	long_delay();

	FPGA_NotConfigLow();
	short_delay();

	while ((*FPGA_ProgramReg & FPGA_NOT_STATUS) != 0) {
		printk("FPGA:  Waiting for NotStatus to go Low ... \n");
	}

	FPGA_NotConfigHigh();

	/* Wait for FPGA "ready to be programmed" signal */
	printk("FPGA:  Waiting for NotStatus to go high (FPGA ready)... \n");

	for (timeout = 0;
	     (((*FPGA_ProgramReg & FPGA_NOT_STATUS) == 0)
	      && (timeout < FPGA_TIMEOUT)); timeout++);

	/* Check if timeout condition occured - i.e. an error */

	if (timeout == FPGA_TIMEOUT) {
		printk
		    ("FPGA:  Failed to program - Timeout waiting for notSTATUS to go high\n");
		return;
	}

	printk("FPGA:  Copying data to FPGA ... %d bytes\n", size);

	/* Copy array to FPGA - bit at a time */

	for (i = 0; i < size; i++) {
		volatile unsigned w = 0;

		for (j = 0; j < 8; j++) {
			*FPGA_ProgramReg = (fpgacode[i] >> j) & 0x01;
			short_delay();
		}
		if ((i & 0x3ff) == 0) {
			printk(".");
		}
	}

	/* Waiting for CONFDONE to go high - means the program is complete  */

	for (timeout = 0;
	     (((*FPGA_ProgramReg & FPGA_CONFDONE) == 0)
	      && (timeout < FPGA_TIMEOUT)); timeout++) {

		*FPGA_ProgramReg = 0x0;
		long_delay();
	}

	if (timeout == FPGA_TIMEOUT) {
		printk
		    ("FPGA:  Failed to program - Timeout waiting for CONFDONE to go high\n");
		return;
	} else {		/* Clock another 10 times - gets the device into a working state      */
		for (i = 0; i < 10; i++) {
			*FPGA_ProgramReg = 0x0;
			short_delay();
		}
	}

	printk("FPGA:  Programming complete\n");
}


static const unsigned char __init fpgacode[] = {
#include "./overdrive.ttf"	/* Code from maxplus2 compiler */
	, 0, 0
};


int __init init_overdrive_fpga(void)
{
	program_overdrive_fpga(fpgacode, sizeof(fpgacode));

	return 0;
}
