/**************************************************************************
//
// cfc200FpgaLoader.c 
//
// Description:  Routines to load the CFC200 FPGA.
//              
//
// Owner:       PFS 
//
// Revisions:   19 Nov 2002 - tross: creation (from cfcFpgaLoader.h)
//
//
// Copyright (c) 2009 Allied Telesyn Networks, Inc.  All Rights Reserved.
//
// Confidentiality and Restricted Use Notice:
//
// This software, including any software of third parties embodied herein,
// contains information, concepts, and algorithms which are confidential
// to Allied Telesyn Networks, Inc. and such third parties.  This software
// is licensed solely for use in accordance with the terms and conditions
// of applicable license agreements with Allied Telesyn Networks, Inc. or
// its authorized distributor.
//
// Restricted Rights legend:
//
// Use, duplication, or disclosure by the U.S. Government is subject to
// restrictions as set forth in subparagraph (c)(1)(ii) of the Rights in
// Technical Data and Computer Software clause at DFARS 252.227-7013
// (October 1988) and FAR 52.227-19 (c) (1987).
//
**************************************************************************/

#ifdef UBOOT_FPGA

#include <common.h>
#include <asm/arch/discovery.h>
#include <asm/arch/gpio.h>

#include "cfcFpgaLoader.h"

#else /* UBOOT_FPGA */

#include "vxWorks.h"
#include <telesyn.h>
#include "fpga/cfc_cpu/cfcFpgaIF.h"
#include "fpga/cfc_cpu/cfcFpgaLoader.h"
#include "stdlib.h"
#include <stdio.h>       /* for printf */

#endif /* UBOOT_FPGA */

#define CFC_FPGA_BITS_RIGHT_TO_LEFT  /* 0x01 programmed as 1,0,0,...,0  */
#undef  CFC_FPGA_BITS_LEFT_TO_RIGHT  /* 0x01 programmed as 0,0,0,...,1  */

#ifdef UBOOT_FPGA

#define CFC_FPGA_REAL

#ifdef CONFIG_MACH_SBX81CFC
	#define FPGA_PRGM_EN	GPIO2
	#define FPGA_CLK	GPIO6
	#define FPGA_DATA	GPIO7
	#define FPGA_DONE	GPIO3
	#define FPGA_INIT	GPIO5
	#define FPGA_PRGM	GPIO4
#else
	#error No CONFIG_MACH_ specified for FPGA progamming
#endif /* CONFIG_MACH_SBX81CFC */ 

#define sysGPIOGet(_gpp, _addr)		disco_get_gpio(_gpp)
#define sysGPIOSet(_gpp, _bit, _addr)	disco_set_gpio(_gpp, _bit)

#else /* UBOOT_FPGA */

/* TODO: Move these, along with getters/setters out to a BSP file */

/* cfc200, uses these */
#define GPIO02 02
#define GPIO03 03
#define GPIO04 04
#define GPIO05 05
#define GPIO06 06
#define GPIO07 07

#ifdef  CFC_FPGA_REAL 
    #if defined(CFC_FPGA_ARMARCH5)
        #define FPGA_PRGM_EN GPIO02
        #define FPGA_CLK     GPIO06
        #define FPGA_DATA    GPIO07
        #define FPGA_DONE    GPIO03
        #define FPGA_INIT    GPIO05
        #define FPGA_PRGM    GPIO04
        
        #define GPIO_OUTPUT_REG 0xD0010100
    #endif
    
#else  /* simulate FPGA */ 

    /* dummy defines for simulator, same as cfc200 */
    #define FPGA_PRGM_EN GPIO02
    #define FPGA_CLK     GPIO06
    #define FPGA_DATA    GPIO07
    #define FPGA_DONE    GPIO03
    #define FPGA_INIT    GPIO05
    #define FPGA_PRGM    GPIO04

    #define FPGA_SIM

#endif /* CFC_FPGA_REAL */ 

#endif /* UBOOT_FPGA */


#ifndef UBOOT_FPGA

/**********************************************************************
 *
 * Function:    cfcFpgaLoaderTest()
 *
 * Description: Test FPGA loading with a short dummy load.
 *
 **********************************************************************/

uint8  FpgaLoadTest[] = {
    0xFF,   0xFF,   0xFF,   0xFF,   0xAA,   0x99,   0x55,   0x66,  /* Xilinx start */
    0x80,   0x40,   0x20,   0x10,   0x08,   0x04,   0x02,   0x01,  /* walking 1    */
    0x7F,   0xBF,   0xDF,   0xEF,   0xF7,   0xFB,   0xFD,   0xFE   /* walking 0    */
};
uint32 FpgaLoadTestLength=24;

#ifndef    CFC_FPGA_REAL

void simFpgaInit()
{
    return;
}
#endif

void cfcFpgaLoaderTest()
{
    cfcFpgaLoader(FpgaLoadTest, FpgaLoadTestLength);
}

void fpgaGPIOHdrShow()
{
    printf("C D D N N \n");
    printf("C I O I P \n");
    printf("L N N N R \n");
    printf("K   E I O \n");
    printf("      T G \n");
    printf("          \n");
    return;
}
void fpgaGPIOShow()
{
    printf("%ld ", sysGPIOGet((uint32) FPGA_CLK, 0)     );
    printf("%ld ", sysGPIOGet((uint32) FPGA_CLK, 0)     );
    printf("%ld ", sysGPIOGet((uint32) FPGA_CLK, 0)     );
    printf("%ld ", sysGPIOGet((uint32) FPGA_CLK, 0)     );
    printf("%ld",  sysGPIOGet((uint32) FPGA_CLK, 0)     );
    printf("\n");

    return;
}

/**********************************************************************
 *
 * Function:    sysGPIOGet, sysGPIOSet
 *
 * Description: GPIO access functions  
 *
 * NOTE:        
 *
 **********************************************************************/
extern int mvGppTypeSet(uint32 group, uint32 mask, uint32 value);
extern uint32 mvGppValueGet(uint32 group, uint32 mask);
extern int mvGppValueSet (uint32 group, uint32 mask, uint32 value);

uint32 sysGPIOGet(uint32 gpio_num, uint32 gpio_addr)
{  
   int status;
   uint32 gppGroup = gpio_num/32;
   uint32 gppBit = gpio_num%32;
   uint32 gppMask=(1<<gppBit);
   uint32 value;

   if ((status = mvGppPolaritySet(gppGroup, gppMask, 0))!=0)
   {
      printf("mvGppPolaritySet failed status=%d\n", status);
      return FALSE;
   }

   /* The type being set to 1 means that it is enabled as an input pin */
   if ((status = mvGppTypeSet(gppGroup, gppMask, 0xFFFFFFFF))!=0)
   {
      printf("mvGppTypeSet failed status=%d\n", status);
      return FALSE;
   }

   value = (mvGppValueGet(gppGroup, gppMask)) >> gppBit;

   return value;
}

void sysGPIOSet(uint32 gpio_num, uint32 bit, uint32 gpio_addr) 
{
   int status;
   uint32 gppGroup = gpio_num/32;
   uint32 gppBit = gpio_num%32;
   uint32 gppMask=(1<<gppBit);
   uint32 gppValue = ((bit&1)<<gppBit);

   /* The type being set to 0 means that it is enabled as an output pin */
   if ((status = mvGppTypeSet(gppGroup, gppMask, 0x00000000))!=0)
   {
      printf("mvGppTypeSet failed status=%d\n", status);
      return;
   }

   if ((status=mvGppValueSet(gppGroup, gppMask, gppValue))!=0)
   {
      printf("mvGppValueSet failed status=%d\n", status);
      return;
   }

#ifndef    CFC_FPGA_REAL
    fpgaGPIOShow();     
#endif /* CFC_FPGA_REAL */
return;
}

#endif /* UBOOT_FPGA */


/**********************************************************************
 *
 * Function:    uint32 cfcFpgaLoaderDone()
 *
 * Description: Return the status of DONE
 *
 * Returns:     1=DONE, 0= not DONE 
 *
 **********************************************************************/
uint32 cfcFpgaLoaderDone()
{
    /* This is called before the loader, make sure the necessary lines are setup */
    sysGPIOSet((uint32) FPGA_CLK,  (uint32) 1, 0);
    sysGPIOSet((uint32) FPGA_DATA, (uint32) 1, 0);
    sysGPIOSet((uint32) FPGA_PRGM, (uint32) 1, 0);

    /* Enable the lines to the FPGA */
    sysGPIOSet((uint32) FPGA_PRGM_EN, (uint32) 1, 0);

    return (sysGPIOGet((uint32) FPGA_DONE, 0));
}


uint32 cfcLoadDoneWait(void)
{
    uint32 counter = 0;
    uint32 max_count = 0x00FFFFFF; /* at least 16 seconds at 266 Mhz */

    sysGPIOSet((uint32) FPGA_DATA, (uint32) 1, 0);

    while (cfcFpgaLoaderDone() == 0)  /* wait until DONE is HIGH */
    {
        #ifndef CFC_FPGA_REAL
            sysGPIOSet((uint32) FPGA_DONE, (uint32) 1, 0);
        #endif /* CFC_FPGA_REAL */

        sysGPIOSet((uint32) FPGA_CLK, (uint32) 0, 0);
        sysGPIOSet((uint32) FPGA_CLK, (uint32) 1, 0);

        if (counter++ >= max_count) 
            return ERROR; 
    }

    /* Need 8 more toggles */
    for(counter = 0;counter < 8;counter++)
    {
       sysGPIOSet((uint32) FPGA_CLK, (uint32) 0, 0);
       sysGPIOSet((uint32) FPGA_CLK, (uint32) 1, 0);
    }

    return OK;
}


/**********************************************************************
 *
 * Function:    cfcFpgaLoader()
 *
 * Description: Loads the FPGA serially by bit banging the 4xxGP GPIO
 *              signals connected to the FPGA.
 *
 *              4xxGP      FPGA
 *              ------    ---------
 *              GPIO 3     CCLK    
 *              GPIO 4     DIN     
 *              GPIO 5     DONE    
 *              GPIO 6    ~INIT    
 *              GPIO 7    ~PROGRAM 
 *
 *              pseudocode:
 *                 assert (LOW) ~PROGRAM 300 ns
 *                 wait until ~INIT is deasserted (HIGH)
 *                 do 
 *                     CCLK = 0
 *                     put bit on DIN
 *                     CCLK = 1
 *                 until last bit loaded
 *                 CCLK = 0
 *                      
 *                 wait until DONE is asserted (HIGH)
 *
 *
 *   
 * Arguments:   uint8 *load   -- pointer to first byte in load
 *              uint32 nbytes -- size of load in bytes
 *
 * Notes:       The size of the load is constant for a given FPGA,
 *              but is used as argument in case the FPGA changes.
 *
 * Returns:     OK    -- if the FPGA loads successfully. 
 *              ERROR -- if the FPGA does not load successfully. 
 *
 *              Calling code should track number of failed load 
 *              attempts with an NVRAM retry counter and boot in
 *              a diagnostic mode after a threshold of failed load 
 *              attempts is exceeded.
 *
 *
 **********************************************************************/
STATUS cfcFpgaLoader(uint8 *load, uint32 nbytes)
{
#ifndef UBOOT_FPGA
    uint32* addr;
#endif /* UBOOT_FPGA */
    uint32 rc; 
    uint32 counter = 0;
    uint32 max_count = 0x000FFFFF; /* at least 16 seconds at 266 Mhz */
    int i, j;
    uint16 byte;
    uint32 bit;
    
    #ifdef FPGA_SIM
        simFpgaInit();    
    #endif
    
    #ifndef  CFC_FPGA_REAL
    /* TODO: remove debug statement */ 
    fpgaGPIOHdrShow();
    #endif

    sysGPIOSet((uint32) FPGA_CLK,  (uint32) 1, 0);
    sysGPIOSet((uint32) FPGA_DATA, (uint32) 1, 0);
    sysGPIOSet((uint32) FPGA_PRGM, (uint32) 1, 0);

    /* Enable the lines to the FPGA */
    sysGPIOSet((uint32) FPGA_PRGM_EN, (uint32) 1, 0);

    sysGPIOSet((uint32) FPGA_PRGM, (uint32) 0, 0); /* deassert PROGRAM tristate to drive a 0 */
    while (counter++ < 40);   /* delay at least 300ns */
    sysGPIOSet((uint32) FPGA_PRGM, (uint32) 1, 0); /* reassert PROGRAM tristate */

#if 0
    counter = 0;
    while (sysGPIOGet((uint32) FPGA_INIT, 0) == 1)    /* wait until ~INIT is LOW */
    {
        #ifndef CFC_FPGA_REAL
            sysGPIOSet((uint32) FPGA_INIT, (uint32) 0, 0);
        #endif /* CFC_FPGA_REAL */
        if (counter++ >= max_count) 
        {
            return ERROR; 
        }
    }
#endif

    counter = 0;    
    while (sysGPIOGet((uint32) FPGA_INIT, 0) == 0)    /* wait until ~INIT is HIGH */
    {
        #ifndef CFC_FPGA_REAL
            sysGPIOSet((uint32) FPGA_INIT, (uint32) 1, 0);
        #endif /* CFC_FPGA_REAL */
        if (counter++ >= max_count)
        {
           return ERROR; 
        }
    }

   /* TODO:  Nested for loop executes over a million times and must be optimized for speed */  
    for (i=0; i<nbytes; i++) 
    {
        byte =  *load;
        load++;

        #ifndef  CFC_FPGA_REAL 
        printf("%0x\n", byte);
        #endif
        for (j=0; j<8; j++) 
        {
#ifdef UBOOT_FPGA
            sysGPIOSet(FPGA_CLK, 0, 0);  /* deassert CCLK */
#else /* UBOOT_FPGA */
            addr = (uint32*)GPIO_OUTPUT_REG;
            *addr &= 0xFFFFFFBF;
#endif /* UBOOT_FPGA */

            #if defined(CFC_FPGA_BITS_RIGHT_TO_LEFT)
            {
               bit = byte&0x01;
               byte = byte >> 1;  
            }
            #elif defined(CFC_FPGA_BITS_LEFT_TO_RIGHT)
            {
               bit = byte&0x80;
               byte = byte << 1;
            }
            #else
                #error  define CFC_FPGA_BITS_RIGHT_TO_LEFT or CFC_FPGA_BITS_LEFT_TO_RIGHT
            #endif /* CFC_FPGA_BITS_RIGHT_TO_LEFT */

#ifdef UBOOT_FPGA
            sysGPIOSet(FPGA_DATA, bit, 0);       /* set DIN */
#else /* UBOOT_FPGA */
            if(bit)
            {
               addr = (uint32*)GPIO_OUTPUT_REG;
               *addr |= 0x00000080;
            }
            else
            {
               addr = (uint32*)GPIO_OUTPUT_REG;
               *addr &= 0xFFFFFF7F;
            }
#endif /* UBOOT_FPGA */

#ifdef UBOOT_FPGA
            sysGPIOSet(FPGA_CLK, 1, 0); /* assert CCLK */
#else /* UBOOT_FPGA */
            addr = (uint32*)GPIO_OUTPUT_REG;
            *addr |= 0x00000040;
#endif /* UBOOT_FPGA */

            /* 5 ns delay (overhead of next instruction is more than 5 ns) */
        }

#ifdef UBOOT_FPGA
        sysGPIOSet(FPGA_CLK, 0, 0);  /* deassert CCLK */
#else /* UBOOT_FPGA */
        addr = (uint32*)GPIO_OUTPUT_REG;
        *addr &= 0xFFFFFFBF;
#endif /* UBOOT_FPGA */
    }

    rc = cfcLoadDoneWait();
    return rc; 
}

