/***************************************************************
 * Project:
 *	  CPLD SlaveSerial Configuration via embedded microprocessor.
 *
 * Copyright info:
 *
 *	  This is free software; you can redistribute it and/or modify
 *	  it as you like.
 *
 *	  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.
 *
 * Description:
 *
 *      This is the main source file that will allow a microprocessor
 *      to configure Xilinx Virtex, Virtex-E, Virtex-EM, Virtex-II,
 *      and Spartan-II devices via the SlaveSerial Configuration Mode.
 *      This code is discussed in Xilinx Application Note, XAPP502.
 *
 * History:
 *	  3-October-2001  MN/MP  - Created
 *	  20-August-2008  Renesas Solutions - Modified to SH7723
 ****************************************************************/

#include <common.h>

/* Serial */
#define SCIF_BASE 0xffe00000 /* SCIF0 */
#define SCSMR	(vu_short *)(SCIF_BASE + 0x00)
#define SCBRR	(vu_char *)(SCIF_BASE + 0x04)
#define SCSCR	(vu_short *)(SCIF_BASE + 0x08)
#define SC_TDR	(vu_char *)(SCIF_BASE + 0x0C)
#define SC_SR	(vu_short *)(SCIF_BASE + 0x10)
#define SCFCR	(vu_short *)(SCIF_BASE + 0x18)
#define	RFCR	(vu_long *)0xFE400020

#define SCSCR_INIT		0x0038
#define SCSCR_CLR		0x0000
#define SCFCR_INIT		0x0006
#define SCSMR_INIT		0x0080
#define RFCR_CLR		0xA400
#define SCI_TD_E		0x0020
#define SCI_TDRE_CLEAR	0x00df

#define BPS_SETTING_VALUE	1 /* 12.5MHz */
#define WAIT_RFCR_COUNTER	500

/* CPLD data size */
#define CPLD_DATA_SIZE	169216

/* out */
#define CPLD_PFC_ADR	((vu_short *)0xA4050112)

#define CPLD_PROG_ADR	((vu_char *)0xA4050132)
#define CPLD_PROG_DAT	0x80

/* in */
#define CPLD_INIT_ADR	((vu_char *)0xA4050132)
#define CPLD_INIT_DAT	0x40
#define CPLD_DONE_ADR	((vu_char *)0xA4050132)
#define CPLD_DONE_DAT	0x20

#define	HIZCRB			((vu_short *)0xA405015A)

/* data */
#define CPLD_NOMAL_START	0xA0A80000
#define CPLD_SAFE_START		0xA0AC0000
#define MODE_SW				(vu_char *)0xA405012A

static void init_cpld_loader(void)
{

	*SCSCR = SCSCR_CLR;
	*SCFCR = SCFCR_INIT;
	*SCSMR = SCSMR_INIT;

	*SCBRR = BPS_SETTING_VALUE;

	*RFCR = RFCR_CLR; /* Refresh counter clear */

	while (*RFCR < WAIT_RFCR_COUNTER)
		;

	*SCFCR = 0x0; /* RTRG=00, TTRG=00 */
				  /* MCE=0,TFRST=0,RFRST=0,LOOP=0 */
	*SCSCR = SCSCR_INIT;
}

static int check_write_ready(void)
{
	u16 status = *SC_SR;
	return status & SCI_TD_E;
}

static void write_cpld_data(char ch)
{
	while (!check_write_ready())
		;

	*SC_TDR = ch;
	*SC_SR;
	*SC_SR = SCI_TDRE_CLEAR;
}

static int delay(void)
{
	int i;
	int c = 0;
	for (i = 0; i < 200; i++) {
		c = *(volatile int *)0xa0000000;
	}
	return c;
}

/***********************************************************************
 *
 * Function:     slave_serial
 *
 * Description:  Initiates SlaveSerial Configuration.
 *               Calls ShiftDataOut() to output serial data
 *
 ***********************************************************************/
static void slave_serial(void)
{
	int i;
	unsigned char *flash;

	*CPLD_PROG_ADR |= CPLD_PROG_DAT; /* PROGRAM_OE HIGH */
	delay();

	/*
	 * Toggle Program Pin by Toggling Program_OE bit
	 * This is accomplished by writing to the Program Register in the CPLD
	 *
	 * NOTE: The Program_OE bit should be driven high to bring the Virtex
	 *      Program Pin low. Likewise, it should be driven low
	 *      to bring the Virtex Program Pin to High-Z
	 */

	*CPLD_PROG_ADR &= ~CPLD_PROG_DAT; /* PROGRAM_OE LOW */
	delay();

	/*
	 * Bring Program High-Z
	 * (Drive Program_OE bit low to bring Virtex Program Pin to High-Z
	 */

	/* Program_OE bit Low brings the Virtex Program Pin to High Z: */
	*CPLD_PROG_ADR |= CPLD_PROG_DAT; /* PROGRAM_OE HIGH */

	while ((*CPLD_INIT_ADR & CPLD_INIT_DAT) == 0)
		delay();

	/* Begin Slave-Serial Configuration */
	flash = (unsigned char *)CPLD_NOMAL_START;

	for (i = 0; i < CPLD_DATA_SIZE; i++)
		write_cpld_data(*flash++);
}

/***********************************************************************
 *
 * Function: check_done_bit
 *
 * Description: This function takes monitors the CPLD Input Register
 * 		   by checking the status of the DONE bit in that Register.
 *               By doing so, it monitors the Xilinx Virtex device's DONE
 *               Pin to see if configuration bitstream has been properly
 *               loaded
 *
 ***********************************************************************/
static void check_done_bit(void)
{
	while (!(*CPLD_DONE_ADR & CPLD_DONE_DAT))
		;
}

/***********************************************************************
 *
 * Function: init_cpld
 *
 * Description: Begins Slave Serial configuration of Xilinx FPGA
 *
 ***********************************************************************/
void init_cpld(void)
{
	/* Init serial device */
	init_cpld_loader();

	if (*CPLD_DONE_ADR & CPLD_DONE_DAT)	/* Already DONE */
		return;

	*HIZCRB = 0x0000;
	*CPLD_PFC_ADR = 0x7c00;			/* FPGA PROG = OUTPUT */

	/* write CPLD data from NOR flash to device */
	slave_serial();

	/*
	 * Monitor the DONE bit in the CPLD Input Register to see if
	 * configuration successful
	 */

	check_done_bit();
}