/***************************************************************************
       flexloader.c - Routines to download code to an Altera FLEX FPGA
       ---------------------------------------------------------------
			    
    begin                : Sun Jan 09 2004
    copyright            : (C) 2005 by Aurelien Jarno
    email                : aurelien@aurel32.net
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/

/* Application specific includes */
#include "flexloader.h"
#include "byteblaster.h"
#include "main.h"

/* Standard includes */
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>


/***************************************************************************
 *                                                                         *
 *   Declarations                                                          *
 *                                                                         *
 ***************************************************************************/

#define TIMEOUT 	500	/* ms */


/***************************************************************************
 *                                                                         *
 *   Private functions                                                     *
 *                                                                         *
 ***************************************************************************/

static int flex_config(int device)
{
	int t; 
	
	set_DCLK(device, 0);
	set_DATA0(device, 1);
	set_nCONFIG(device, 1);
	enable_byteblaster(device, 1);
	usleep(100000);

	if (debug)
		printf("Configuring FLEX\n");
	
	for (t = TIMEOUT / 10 ; t > 0 ; t--)
	{
		if (get_nSTATUS(device) == 1)
			break;
		usleep(10000);
	}
	if (t == 0) 
	{
		if (!quiet)
			fprintf(stderr, "nSTATUS != 1\n");
		return -1;
	}

	set_nCONFIG(device, 0);
	
	for (t = TIMEOUT / 10 ; t > 0 ; t--)
	{
		if (get_nSTATUS(device) == 0)
			break;
		usleep(10000);
	}
	if (t == 0)
	{
		if (!quiet)
			fprintf(stderr, "nSTATUS != 0\n");
		return -1;
	}
	
	for (t = TIMEOUT / 10 ; t > 0 ; t--)
	{
		if (get_CONF_DONE(device) == 0)
			break;
		usleep(10000);
	}
	if (t == 0)
	{
		if (!quiet)
			fprintf(stderr, "CONF_DONE != 0\n");
		return -1;
	}

	set_nCONFIG(device, 1);
	
	for (t = TIMEOUT / 10 ; t > 0 ; t--)
	{
		if (get_nSTATUS(device) == 1)
			break;
		usleep(10000);
	}
	if (t == 0)
	{
		if (!quiet)
			fprintf(stderr, "nSTATUS != 1\n");
		return -1;
	}

	usleep(40000);
	return 0;
}

static int flex_download(int device, int file)
{
	int byte, bit;
	unsigned char data;

	if (debug)
		printf("Downloading code\n");

	for(byte = 0 ; ; byte++)
	{
		if (debug)
			printf("Downloading byte %i\n", byte);

		if (read(file, &data, 1) != 1)
		{
			if (!quiet)
				fprintf(stderr, "Unexpected end of file\n");
			return -1;
		}
		
		for (bit = 0 ; bit < 8 ; bit++)
		{
			set_DATA0(device, data & 0x01);
			set_DCLK(device, 1);
			set_DCLK(device, 0);
			data >>= 1;
			
			if (get_nSTATUS(device) == 0)
			{
				if (!quiet)
					fprintf(stderr, "Checksum error\n");
				return -1;
			}

			if (get_CONF_DONE(device) == 1)
			{
				if (!quiet)
					printf("%i bytes downloaded\n", byte);
				return 0;
			}
		}
	}
}
	
static void flex_init(int device)
{
	int i;
	
	if (debug)
		printf("Initializing FLEX\n");

	/* more clocks required to initialize the device */
	for (i = 0 ; i < 10 ; i++)
	{
		set_DCLK(device, 1);
		set_DCLK(device, 0);
	}
}


/***************************************************************************
 *                                                                         *
 *   Public functions                                                      *
 *                                                                         *
 ***************************************************************************/

int flex_download_program(char *device, char *rbf_file)
{
	int fd;
	int byteblaster;

	if (debug)
		printf("Opening %s\n", rbf_file);

	fd = open(rbf_file, O_RDONLY);

	if (fd < 0)
	{
		if (!quiet)
		{
			perror("open");
			fprintf(stderr, "Can't open %s!\n", rbf_file);
		}
		return -1;
	}

	byteblaster = open_byteblaster(device);

	if (byteblaster < 0)
		return -1;
		
	if (flex_config(byteblaster) < 0)
	{
		if (!quiet)
			fprintf(stderr, "Altera FLEX not found!\n");
		return -1;
	}
	
	if (flex_download(byteblaster, fd) < 0)
	{
		if (!quiet)
			fprintf(stderr, "Download failed\n");
		return -1;
	}
	
	flex_init(byteblaster);

	close_byteblaster(byteblaster);

	close(fd);
	return 0;
}