/*  ============================================================================
 *   Copyright (c) Texas Instruments Inc 2013
 *
 *   Use of this software is controlled by the terms and conditions found in the
 *   license agreement under which this software has been supplied.
 *   ===========================================================================
 */
/** ============================================================================
 Serdes configuration on K2 DSP
 * =============================================================================
 *  Revision History
 *  ===============
 *  June 1, 2013 Brighton  file created
 * =============================================================================
 */
#include <stdio.h>
#include <stdint.h>
#include <csl_serdes_srio.h>
#include <csl_serdes_hyperLink.h>
#include <csl_serdes_ethernet.h>
#include <csl_serdes_pcie.h>
#include <csl_serdes_aif2.h>
#include "K2_Serdes_init.h"

char * SerdesErrorStr[]=
{
	"SERDES no error",
	"SERDES error: invalid input reference clock speed!",
	"SERDES error: invalid link speed!"
};

/*Serdes initialization for SRIO*/
void K2_SRIO_Serdes_init(K2_SerdesConfig * serdes_cfg)
{
	int i;
	CSL_SERDES_LINK_RATE linkSpeed;
	Uint32 uiResult;

	// Disable pll before configuring the SerDes registers
	CSL_SrioSerdesShutdown(CSL_SRIO_SERDES_CFG_REGS);

	if(serdes_cfg->linkSpeed_GHz== 1.25f)
	{
		linkSpeed= CSL_SERDES_LINK_RATE_5G;
		for(i=0;i<4;i++)
			serdes_cfg->laneRateScale[i] = CSL_SERDES_LANE_QUARTER_RATE;

	}
	else if(serdes_cfg->linkSpeed_GHz== 2.5f)
	{
		linkSpeed= CSL_SERDES_LINK_RATE_5G;

		//scale the lane rate by 1/2
		for(i=0;i<4;i++)
			serdes_cfg->laneRateScale[i] += 1;
	}
	else if(serdes_cfg->linkSpeed_GHz== 3.125f)
	{
		linkSpeed= CSL_SERDES_LINK_RATE_6p25G;

		//scale the lane rate by 1/2
		for(i=0;i<4;i++)
			serdes_cfg->laneRateScale[i] += 1;
	}
	else if(serdes_cfg->linkSpeed_GHz== 5.f)
	{
		linkSpeed= CSL_SERDES_LINK_RATE_5G;
	}
	else
		printf("Error: link speed %.3fGbps is not supported by SRIO\n",serdes_cfg->linkSpeed_GHz);
	
	uiResult= CSL_SrioSerdesInit(CSL_SRIO_SERDES_CFG_REGS,
		serdes_cfg->inputRefClock,
		linkSpeed);
	if(uiResult)
		printf("  %s\n", SerdesErrorStr[uiResult]);

	for(i=0; i<4; i++)
	{
		if((serdes_cfg->laneEnableMask>>i)&1)
		{
			CSL_SrioSerdesLaneConfig(CSL_SRIO_SERDES_CFG_REGS,
				serdes_cfg->inputRefClock, linkSpeed, i);
		}
	}

	CSL_SrioSerdesComEnable(CSL_SRIO_SERDES_CFG_REGS);

	for(i=0; i<4; i++)
	{
		if((serdes_cfg->laneEnableMask>>i)&1)
		{
			uiResult= CSL_SrioSerdesLaneEnable(CSL_SRIO_SERDES_CFG_REGS,
				i, linkSpeed, serdes_cfg->loopBackMode, 
				serdes_cfg->laneRateScale[i]);
			if(uiResult)
				printf("  lane %d invalid rate scale\n", i);
		}
	}

	// Enable pll vi athe pll_ctrl 0x0014
	CSL_SrioSerdesPllEnable(CSL_SRIO_SERDES_CFG_REGS);

	// Wait the SerDes PLL lock
	while(CSL_SERDES_STATUS_PLL_NOT_LOCKED==K2_SerdesPLLGetStatus(CSL_SRIO_SERDES_CFG_REGS));
	
	for(i=0; i<4; i++)
	{
		if((serdes_cfg->laneEnableMask>>i)&1)
		{
			while(CSL_SERDES_STATUS_PLL_NOT_LOCKED==K2_SerdesLaneGetStatus(CSL_SRIO_SERDES_CFG_REGS, i));
		}
	}
}

/*Serdes initialization for AIF2*/
void K2_AIF2_Serdes_init(K2_AIF2_SerdesConfig * serdes_cfg)
{
	int i;
	CSL_SERDES_LINK_RATE linkSpeed;
	Uint32 uiResult;

#if 0
	// Disable pll before configuring the SerDes registers
	if(serdes_cfg->laneEnableMask&0xF) //lane 0~3
		CSL_AIF2SerdesShutdown(CSL_AIF_SERDES_B8_CFG_REGS);
	if(serdes_cfg->laneEnableMask&0x30) //lane 4~5
		CSL_AIF2SerdesShutdown(CSL_AIF_SERDES_B4_CFG_REGS);
#endif

	if(serdes_cfg->linkSpeed_GHz== 4.9152f)
	{
		linkSpeed= CSL_SERDES_LINK_RATE_4p9152G;
	}
	else if(serdes_cfg->linkSpeed_GHz== 6.144f)
	{
		linkSpeed= CSL_SERDES_LINK_RATE_6p144G;
	}
	else
		printf("Error: link speed %.4fGbps is not supported by AIF2\n",serdes_cfg->linkSpeed_GHz);
	
	if(serdes_cfg->laneEnableMask&0xF) //lane 0~3
	{
		uiResult= CSL_AIF2SerdesInit(CSL_AIF_SERDES_B8_CFG_REGS,
			serdes_cfg->inputRefClock, linkSpeed);
		if(uiResult)
			printf("  %s\n", SerdesErrorStr[uiResult]);
	}
	
	if(serdes_cfg->laneEnableMask&0x30) //lane 4~5
	{
		uiResult= CSL_AIF2SerdesInit(CSL_AIF_SERDES_B4_CFG_REGS,
			serdes_cfg->inputRefClock, linkSpeed);
		if(uiResult)
			printf("  %s\n", SerdesErrorStr[uiResult]);
	}
	
	for(i=0; i<4; i++) //Serdes B8
	{
		if((serdes_cfg->laneEnableMask>>i)&1)
		{
			CSL_AIF2SerdesLaneConfig(CSL_AIF_SERDES_B8_CFG_REGS,
				serdes_cfg->inputRefClock, linkSpeed, i);
		}
	}
	for(i=4; i<6; i++) //Serdes B8
	{
		if((serdes_cfg->laneEnableMask>>i)&1)
		{
			CSL_AIF2SerdesLaneConfig(CSL_AIF_SERDES_B4_CFG_REGS,
				serdes_cfg->inputRefClock, linkSpeed, i-4);
		}
	}

	if(serdes_cfg->laneEnableMask&0xF) //lane 0~3
		CSL_AIF2SerdesComEnable(CSL_AIF_SERDES_B8_CFG_REGS);
	if(serdes_cfg->laneEnableMask&0x30) //lane 4~5
		CSL_AIF2SerdesComEnable(CSL_AIF_SERDES_B4_CFG_REGS);

	for(i=0; i<6; i++)
	{
		if((serdes_cfg->laneEnableMask>>i)&1)
		{
			CSL_AIF2SerdesLaneEnable(CSL_AIF_CFG_REGS,
				i, serdes_cfg->laneRateScale[i]);
		}
	}

	for(i=0; i<4; i++) //Serdes B8
	{
		if((serdes_cfg->loopBackMask>>i)&1)
		{
			CSL_AIF2SerdesLoopbackEnable(CSL_AIF_SERDES_B8_CFG_REGS,
				i, CSL_SERDES_LOOPBACK_ENABLED);
		}
		else
		{
			CSL_AIF2SerdesLoopbackEnable(CSL_AIF_SERDES_B8_CFG_REGS,
				i, CSL_SERDES_LOOPBACK_DISABLED);
		}
	}
	for(i=4; i<6; i++) //Serdes B4
	{
		if((serdes_cfg->loopBackMask>>i)&1)
		{
			CSL_AIF2SerdesLoopbackEnable(CSL_AIF_SERDES_B4_CFG_REGS,
				i-4, CSL_SERDES_LOOPBACK_ENABLED);
		}
		else
		{
			CSL_AIF2SerdesLoopbackEnable(CSL_AIF_SERDES_B4_CFG_REGS,
				i-4, CSL_SERDES_LOOPBACK_DISABLED);
		}
	}

	// Enable Serdes PLL, and Wait the SerDes PLL lock
	if(serdes_cfg->laneEnableMask&0xF) //lane 0~3
	{
		CSL_AIF2SerdesPllEnableB8(CSL_AIF_CFG_REGS);
		while(CSL_SERDES_STATUS_PLL_NOT_LOCKED==CSL_AIF2SerdesGetStatusB8(CSL_AIF_CFG_REGS));
	}
	if(serdes_cfg->laneEnableMask&0x30) //lane 4~5
	{
		CSL_AIF2SerdesPllEnableB4(CSL_AIF_CFG_REGS);
		while(CSL_SERDES_STATUS_PLL_NOT_LOCKED==CSL_AIF2SerdesGetStatusB4(CSL_AIF_CFG_REGS));
	}

	/* The TX byte clock from either B8 or B4 SERDES link will be selected as 
	sys_clk once the PLL has acquired lock.*/
	if(0==serdes_cfg->laneEnableMask&0xF) //Serdes B8 is not used
		CSL_AIF2SerdesClkSelB4(CSL_AIF_CFG_REGS);

	//AIF2 Links Clock Enable
	for(i=0; i < 6; i++)
	{
		if((serdes_cfg->laneEnableMask>>i)&1)
			CSL_AIF2LinkClkEnable(CSL_AIF_CFG_REGS, i);
		else
			CSL_AIF2LinkClkDisable(CSL_AIF_CFG_REGS, i);
	}
}

/*Serdes initialization for HyperLink*/
void K2_HyperLink_Serdes_Init(Uint32 link_num, 
	K2_HyperLinkSerdesConfig * serdes_cfg)
{
	Uint32 i;
	Uint32 uiBase_addr;
	CSL_SERDES_LINK_RATE linkSpeed;
	CSL_SERDES_LANE_CTRL_RATE laneRateScale;
	Uint32 uiResult;

	if(0==link_num)
		uiBase_addr= CSL_HYPERLINK_0_SERDES_CFG_REGS;
	else
		uiBase_addr= CSL_HYPERLINK_1_SERDES_CFG_REGS;
	
	/* Disable PLL Before Configuring Serdes Reg */
	CSL_HyperlinkSerdesShutdown(uiBase_addr);

	if(serdes_cfg->linkSpeed_GHz== 3.125f)
	{
		linkSpeed= CSL_SERDES_LINK_RATE_6p25G;

		//scale the lane rate by 1/2
		laneRateScale= CSL_SERDES_LANE_HALF_RATE;
	}
	else if(serdes_cfg->linkSpeed_GHz== 6.25f)
	{
		linkSpeed= CSL_SERDES_LINK_RATE_6p25G;
		laneRateScale= CSL_SERDES_LANE_FULL_RATE;
	}
	else
		printf("Error: link speed %.3fGbps is not supported by HyperLink\n",serdes_cfg->linkSpeed_GHz);

	//SB CMU and COMLANE Setup
	uiResult= CSL_HyperlinkSerdesInit(uiBase_addr, serdes_cfg->inputRefClock, linkSpeed);
	if(uiResult)
		printf("  %s\n", SerdesErrorStr[uiResult]);

	//SB Lane Setup
	for(i=0; i < serdes_cfg->numLanes; i++)
	{
		CSL_HyperlinkSerdesLaneConfig(uiBase_addr, 
			serdes_cfg->inputRefClock, linkSpeed, i);
	}

	//SB CMU and COMLANE Enable
	CSL_HyperlinkSerdesComEnable(uiBase_addr);

	//SB Lane Enable
	for(i=0; i <serdes_cfg->numLanes; i++)
	{
		CSL_HyperlinkSerdesLaneEnable(uiBase_addr, i, 
			serdes_cfg->loopBackMode, laneRateScale);
	}

	//SB PLL Enable
	CSL_HyperlinkSerdesPllEnable(uiBase_addr);

#if 0
	// Wait the SerDes PLL lock
	while(CSL_SERDES_STATUS_PLL_NOT_LOCKED==K2_SerdesPLLGetStatus(uiBase_addr));
	
	for(i=0; i<serdes_cfg->numLanes; i++)
	{
		while(CSL_SERDES_STATUS_PLL_NOT_LOCKED==K2_SerdesLaneGetStatus(uiBase_addr, i));
	}
#else
	CSL_HyperlinkSerdesGetStatus(uiBase_addr, serdes_cfg->numLanes);
#endif

}

/*Serdes initialization for Giga bit ethernet*/
void K2_GE_Serdes_init(K2_GE_SerdesConfig * serdes_cfg)
{
	int i;
	CSL_SERDES_LINK_RATE linkSpeed;
	CSL_SERDES_LANE_CTRL_RATE laneRateScale;
	Uint32 uiResult;

	// Disable pll before configuring the SerDes registers
	CSL_EthernetSerdesShutdown(CSL_NETCP_SERDES_CFG_REGS);

	if(serdes_cfg->linkSpeed_GHz== 1.25f)
	{
		linkSpeed= CSL_SERDES_LINK_RATE_1p25G;
		laneRateScale = CSL_SERDES_LANE_QUARTER_RATE; //???
	}
	else
		printf("Error: link speed %.3fGbps is not supported by SGMII\n",serdes_cfg->linkSpeed_GHz);
	
	uiResult= CSL_EthernetSerdesInit(CSL_NETCP_SERDES_CFG_REGS,
		serdes_cfg->inputRefClock, linkSpeed);
	if(uiResult)
		printf("  %s\n", SerdesErrorStr[uiResult]);

	for(i=0; i<4; i++)
	{
		if((serdes_cfg->laneEnableMask>>i)&1)
		{
			CSL_EthernetSerdesLaneConfig(CSL_NETCP_SERDES_CFG_REGS,
				serdes_cfg->inputRefClock, linkSpeed, i);
		}
	}

	CSL_EthernetSerdesComEnable(CSL_NETCP_SERDES_CFG_REGS);

	for(i=0; i<4; i++)
	{
		if((serdes_cfg->laneEnableMask>>i)&1)
		{
			uiResult= CSL_EthernetSerdesLaneEnable(CSL_NETCP_SERDES_CFG_REGS,
				i, serdes_cfg->loopBackMode, laneRateScale);
			if(uiResult)
				printf("  lane %d error: invalid rate scale\n", i);
		}
	}

	// Enable pll vi athe pll_ctrl 0x0014
	CSL_EthernetSerdesPllEnable(CSL_NETCP_SERDES_CFG_REGS);

	// Wait the SerDes PLL lock
	while(CSL_SERDES_STATUS_PLL_NOT_LOCKED==K2_SerdesPLLGetStatus(CSL_NETCP_SERDES_CFG_REGS));
	
	for(i=0; i<4; i++)
	{
		if((serdes_cfg->laneEnableMask>>i)&1)
		{
			while(CSL_SERDES_STATUS_PLL_NOT_LOCKED==K2_SerdesLaneGetStatus(CSL_NETCP_SERDES_CFG_REGS, i));
		}
	}
}

/*Serdes initialization for PCIE*/
void K2_PCIE_Serdes_init(K2_PCIE_SerdesConfig * serdes_cfg)
{
	int i;
	CSL_SERDES_LINK_RATE linkSpeed;
	CSL_SERDES_PCIE_LANE_RATE laneRateScale;
	Uint32 uiResult;

	// Disable pll before configuring the SerDes registers
	CSL_PCIeSerdesShutdown(CSL_PCIE_SERDES_CFG_REGS);

	if(serdes_cfg->linkSpeed_GHz== 5.f)
	{
		linkSpeed= CSL_SERDES_LINK_RATE_5G;
		laneRateScale = CSL_SERDES_PCIE_GEN_2;
	}
	else
		printf("Error: link speed %.3fGbps is not supported by PCIE\n",serdes_cfg->linkSpeed_GHz);

	uiResult= CSL_PCIeSerdesInit(CSL_PCIE_SERDES_CFG_REGS,
		serdes_cfg->inputRefClock,	linkSpeed);
	if(uiResult)
		printf("  %s\n", SerdesErrorStr[uiResult]);

	for(i=0; i<serdes_cfg->numLanes; i++)
	{
		CSL_PCIeSerdesLaneConfig(CSL_PCIE_SERDES_CFG_REGS,
			serdes_cfg->inputRefClock, linkSpeed, i);
	}

	CSL_PCIeSerdesComEnable(CSL_PCIE_SERDES_CFG_REGS);

	for(i=0; i<serdes_cfg->numLanes; i++)
	{
		CSL_PCIeSerdesSetLoopback (CSL_PCIE_SERDES_CFG_REGS, i, serdes_cfg->loopBackMode);
	}

	CSL_PCIeSerdesLaneEnable (CSL_PCIE_SERDES_CFG_REGS, serdes_cfg->numLanes);

	CSL_PCIeSerdesSetLaneRate (CSL_PCIE_SERDES_CFG_REGS, laneRateScale);

	// Wait the SerDes PLL lock
	while(CSL_SERDES_STATUS_PLL_NOT_LOCKED==K2_SerdesPLLGetStatus(CSL_PCIE_SERDES_CFG_REGS));
	
	for(i=0; i<serdes_cfg->numLanes; i++)
	{
		while(CSL_SERDES_STATUS_PLL_NOT_LOCKED==K2_PCIE_SerdesLaneGetStatus(CSL_PCIE_SERDES_CFG_REGS, i));
	}
}

