/******************************************************************************

  Copyright (C), 2001-2012, Texas Instrument.

 ******************************************************************************
  File Name     : KeyStone_Serdes_init.c
  Version       : Initial Draft
  Author        : Brighton
  Created       : 2011/6/10
  Last Modified :
  Description   :  example for Serdes configuration on KeyStone DSP

  Function List :
              KeyStone_HyperLink_Serdes_init
              KeyStone_Serdes_disable
              KeyStone_SGMII_Serdes_init
              KeyStone_SRIO_Serdes_init
  History       :
  1.Date        : 2011/6/10
    Author      : Brighton
    Modification: Created file

  2.Date         : 2011/6/13
    Author       : Zhan
    Modification : Modify the SRIO SERDES configuration

******************************************************************************/
#include "KeyStone_Serdes_init.h"
/*----------------------------------------------*
 * external variables                           *
 *----------------------------------------------*/

/*----------------------------------------------*
 * external routine prototypes                  *
 *----------------------------------------------*/

/*----------------------------------------------*
 * internal routine prototypes                  *
 *----------------------------------------------*/

/*----------------------------------------------*
 * project-wide global variables                *
 *----------------------------------------------*/

/*----------------------------------------------*
 * module-wide global variables                 *
 *----------------------------------------------*/

/*----------------------------------------------*
 * constants                                    *
 *----------------------------------------------*/

/*----------------------------------------------*
 * macros                                       *
 *----------------------------------------------*/

/*----------------------------------------------*
 * routines' implementations                    *
 *----------------------------------------------*/
/*****************************************************************************
 Prototype    : KeyStone_SRIO_Serdes_init
 Description  : Configure the SRIO SERDES
 Input        : SerdesSetup_4links * serdes_cfg  
                SerdesRegs * serdesRegs          
 Output       : None
 Return Value : 
 Calls        : 
 Called By    : 
 
  History        :
  1.Date         : 2011/6/11
    Author       : Brighton
    Modification : Created function

  2.Date         : 2011/6/13
    Author       : Zhan
    Modification : Add the status check
*****************************************************************************/
void KeyStone_SRIO_Serdes_init(
	SerdesSetup_4links * serdes_cfg, SerdesRegs * serdesRegs)
{
	int i;
	float pllMpy, serdesPllClock_GHz=12.5, rateScale;
	SerdesLinkRate linkRateScale; 
	SerdesVcoRange vcoRange;

	if(NULL== serdes_cfg)
		return;
		
	CSL_BootCfgUnlockKicker();

	/*find lowest link speed, the SERDES PLL output should be less or equal to it*/
	for(i= 0; i<4; i++)
	{
		if(serdes_cfg->linkSetup[i])
			if(serdesPllClock_GHz>serdes_cfg->linkSetup[i]->linkSpeed_GHz)
				serdesPllClock_GHz=serdes_cfg->linkSetup[i]->linkSpeed_GHz;
	}

	/*Serdes PLL output must be between 1.5625 and 3.125*/
	if(serdesPllClock_GHz>6.25)
		serdesPllClock_GHz /= 4;
	else if(serdesPllClock_GHz>3.125)
		serdesPllClock_GHz /= 2;
	else if(serdesPllClock_GHz<1.5625)
		serdesPllClock_GHz *= 2;

	/*set VCO range according to the PLL output speed*/
	if(serdesPllClock_GHz<2.17)
		vcoRange= SERDES_PLL_VCO_RANGE_HIGH;
	else
		vcoRange= SERDES_PLL_VCO_RANGE_LOW;

	/*calculate PLL MPY factor according to input reference clock speed*/
	pllMpy= serdesPllClock_GHz*1000/serdes_cfg->commonSetup.inputRefClock_MHz;

	/*PLL multiply factors between 4 and 60*/
	if(pllMpy<4 || pllMpy>60)
		pllMpy= 4;

	serdesRegs->CFGPLL = 1| /*ENPLL*/
		(serdes_cfg->commonSetup.loopBandwidth<<11)|
		(vcoRange<<9)|
		((Uint32)(pllMpy*4)<<1);

	for(i=0; i<4; i++)
	{
		if(serdes_cfg->linkSetup[i])
		{
			rateScale=serdes_cfg->linkSetup[i]->linkSpeed_GHz/serdesPllClock_GHz;
			if(rateScale<1)
				linkRateScale = SRIO_SERDES_LINK_RATE_div2;
			else if(rateScale<2)
				linkRateScale = SRIO_SERDES_LINK_RATE_x1;
			else if(rateScale<4)
				linkRateScale = SRIO_SERDES_LINK_RATE_x2;
			else
				linkRateScale = SRIO_SERDES_LINK_RATE_x4;
				
			serdesRegs->link[i].CFGRX= 1| /*ENRX*/
				(2<<1)| 	/*Bus width must be 010b (20 bit)*/
				(serdes_cfg->linkSetup[i]->testPattern<<25)|
				(serdes_cfg->linkSetup[i]->loopBack<<23)|
				(1<<22)| 	/*Enable offset compensation*/
				(serdes_cfg->linkSetup[i]->rxEqualizerConfig<<18)|
				(serdes_cfg->linkSetup[i]->rxCDR<<15)|
				(serdes_cfg->linkSetup[i]->rxLos<<12)|
				(serdes_cfg->linkSetup[i]->rxAlign<<10)|
				(serdes_cfg->linkSetup[i]->rxTermination<<7)|
				(serdes_cfg->linkSetup[i]->rxInvertPolarity<<6)|
				(linkRateScale<<4);

			if(i==0)
				serdesRegs->link[i].CFGTX= 1| /*ENTX*/
					(2<<1)| 	/*Bus width must be 010b (20 bit)*/
					(serdes_cfg->linkSetup[i]->testPattern<<23)|
					(serdes_cfg->linkSetup[i]->loopBack<<21)|
					(1 << 20)| 	/*Synchronization Master.*/
					(serdes_cfg->linkSetup[i]->txOutputSwing<<7)|
					(serdes_cfg->linkSetup[i]->txInvertPolarity<<6)|
					(linkRateScale<<4);
			else
				serdesRegs->link[i].CFGTX= 1| /*ENTX*/
					(2<<1)| 	/*Bus width must be 010b (20 bit)*/
					(serdes_cfg->linkSetup[i]->testPattern<<23)|
					(serdes_cfg->linkSetup[i]->loopBack<<21)|
					(0 << 20)| 	/*Synchronization Master.*/
					(serdes_cfg->linkSetup[i]->txOutputSwing<<7)|
					(serdes_cfg->linkSetup[i]->txInvertPolarity<<6)|
					(linkRateScale<<4);
		}
	}

}
/*****************************************************************************
 Prototype    : KeyStone_HyperLink_Serdes_init
 Description  : Hyperlink SERDES configuration
 Input        : SerdesSetup_4links * serdes_cfg  
                SerdesRegs * serdesRegs          
 Output       : None
 Return Value : 
 Calls        : 
 Called By    : 
 
  History        :
  1.Date         : 2011/6/11
    Author       : Brighton
    Modification : Created function

*****************************************************************************/
void KeyStone_HyperLink_Serdes_init(
	SerdesSetup_4links * serdes_cfg, SerdesRegs * serdesRegs)
{
	int i;
	float pllMpy, serdesPllClock_GHz=12.5, rateScale;
	SerdesLinkRate linkRateScale; 
	SerdesVcoRange vcoRange;

	if(NULL== serdes_cfg)
		return;
		
	CSL_BootCfgUnlockKicker();

	/*find lowest link speed, the SERDES PLL output should be less or equal to it*/
	for(i= 0; i<4; i++)
	{
		if(serdes_cfg->linkSetup[i])
			if(serdesPllClock_GHz > serdes_cfg->linkSetup[i]->linkSpeed_GHz)
				serdesPllClock_GHz = serdes_cfg->linkSetup[i]->linkSpeed_GHz;
	}

	/*Serdes PLL output must be less than 3.125*/
	if(serdesPllClock_GHz>6.25)
		serdesPllClock_GHz /= 4;
	else if(serdesPllClock_GHz>3.125)
		serdesPllClock_GHz /= 2;
	else if(serdesPllClock_GHz<1.5625)
		serdesPllClock_GHz *= 2;

	/*set VCO range according to the PLL output speed*/
	if(serdesPllClock_GHz<2.17)
		vcoRange= SERDES_PLL_VCO_RANGE_HIGH;
	else
		vcoRange= SERDES_PLL_VCO_RANGE_LOW;

	/*calculate PLL MPY factor according to input reference clock speed*/
	pllMpy= serdesPllClock_GHz*1000/serdes_cfg->commonSetup.inputRefClock_MHz;

	/*PLL multiply factors between 4 and 60*/
	if(pllMpy<4 || pllMpy>60)
		pllMpy= 4;

	for(i=0; i<4; i++)
	{
		if(serdes_cfg->linkSetup[i])
		{
			rateScale=serdes_cfg->linkSetup[i]->linkSpeed_GHz/serdesPllClock_GHz;
			if(rateScale<1)
				linkRateScale = HYPERLINK_SERDES_LINK_RATE_div2;
			else if(rateScale<2)
				linkRateScale = HYPERLINK_SERDES_LINK_RATE_x1;
			else if(rateScale<4)
				linkRateScale = HYPERLINK_SERDES_LINK_RATE_x2;
			else
				linkRateScale = HYPERLINK_SERDES_LINK_RATE_x4;

			/*Serdes Loopback is controled by Hyperlink control
			register, Serdes loopback register has no effect*/
			/*HyperLink automatically sets LOS to 0 for loopback. 
			User must set LOS to 4 for non-loopack*/
			serdes_cfg->linkSetup[i]->rxLos = (SerdesRxLos)4;

			serdesRegs->link[i].CFGRX= 1| /*ENRX*/
				(2<<1)| 	/*Bus width must be 010b (20 bit)*/
				(serdes_cfg->linkSetup[i]->testPattern<<25)|
				(1<<22)| 	/*Enable offset compensation*/
				(serdes_cfg->linkSetup[i]->rxEqualizerConfig<<18)|
				(serdes_cfg->linkSetup[i]->rxCDR<<15)|
				(serdes_cfg->linkSetup[i]->rxLos<<12)|
				(serdes_cfg->linkSetup[i]->rxAlign<<10)|
				(serdes_cfg->linkSetup[i]->rxTermination<<7)|
				(serdes_cfg->linkSetup[i]->rxInvertPolarity<<6)|
				(linkRateScale<<4);

			serdesRegs->link[i].CFGTX= 1| /*ENTX*/
				(2<<1)| 	/*Bus width must be 010b (20 bit)*/
				(serdes_cfg->linkSetup[i]->testPattern<<23)|
				(serdes_cfg->linkSetup[i]->loopBack<<21)|
				(1 << 20)| 	/*Synchronization Master.*/
				(1<<19)| 	/*FIRUPT = 1 Transmitter pre and post cursor FIR filter update*/
				/*TWPST1: Adjacent Post Cursor Tap Weight. 
				If trace length is 4Ħħ, start with 20 (-10%). 
				If trace length is between 4Ħħ and 10Ħħ, start with 27 (-27.5%).*/
				(18<<14)| 	
				(1<<11)| 	/*TWPRE: The settings range from 0 to -17.5% in 2.5% steps.*/
				(serdes_cfg->linkSetup[i]->txOutputSwing<<7)|
				(serdes_cfg->linkSetup[i]->txInvertPolarity<<6)|
				(linkRateScale<<4);
		}
	}

	/*write to CFGPLL will start the Hyperlink training sequence, so, 
	this should be the last register been write for HyperLink initialization*/
	serdesRegs->CFGPLL = 1| /*ENPLL*/
		(serdes_cfg->commonSetup.loopBandwidth<<11)|
		(vcoRange<<9)|
		((Uint32)(pllMpy*4)<<1);

}

/*****************************************************************************
 Prototype    : KeyStone_Serdes_disable
 Description  : SERDES disable for KeyStone device
 Input        : SerdesRegs * serdesRegs  
                Uint32 uiLinkNum         
 Output       : None
 Return Value : 
 Calls        : 
 Called By    : 
 
  History        :
  1.Date         : 2011/6/13
    Author       : Brighton
    Modification : Created function

*****************************************************************************/
void KeyStone_Serdes_disable(
	SerdesRegs * serdesRegs, Uint32 uiLinkNum)
{
	int i;
	
	CSL_BootCfgUnlockKicker();

	//disable PLL
	serdesRegs->CFGPLL = 0;

	//disable TX/RX links
	for(i= 0; i< uiLinkNum; i++)
	{
		serdesRegs->link[i].CFGRX= 0;
		serdesRegs->link[i].CFGTX= 0;
	}
}


