/*  ============================================================================
 *   Copyright (c) Texas Instruments Inc 2011~2013
 *
 *   Use of this software is controlled by the terms and conditions found in the
 *   license agreement under which this software has been supplied.
 *   ===========================================================================
 */
/** ============================================================================
 example for HyperLink configuration on KeyStone device
 * =============================================================================
 *  Revision History
 *  ===============
 *  Nov 4, 2011 Brighton Feng  file created
 *  March 6, 2013 Brighton Feng  Update for K2
 * =============================================================================*/
#include <c6x.h>
#include <stdio.h>
#include <csl_pscAux.h>
#include "K2_HyperLink_init.h"
#include <csl_serdes_hyperLink.h>

//there may be one or two HyperLink interface on some devices
CSL_VusrRegs * gpHyperLinkRegs[2] = 
{
	(CSL_VusrRegs *)CSL_HYPERLINK_0_SLV_CFG_REGS
#ifdef CSL_HYPERLINK_1_SLV_CFG_REGS
	,(CSL_VusrRegs *)CSL_HYPERLINK_1_SLV_CFG_REGS
#endif
};
//SerdesRegs * hyperLinkSerdesRegs;

char * SerdesErrorStr[]=
{
	"SERDES no error",
	"SERDES error: invalid input reference clock speed!",
	"SERDES error: invalid link speed!"
};
/*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;

	uiBase_addr= CSL_HYPERLINK_0_SERDES_CFG_REGS;
#ifdef CSL_HYPERLINK_1_SERDES_CFG_REGS
	if(1==link_num) 	//for K2H, K2K
		uiBase_addr= CSL_HYPERLINK_1_SERDES_CFG_REGS;
#endif
	
	/* 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== 5.f)
	{
		linkSpeed= CSL_SERDES_LINK_RATE_5G;
		laneRateScale= CSL_SERDES_LANE_FULL_RATE;
	}
	else if(serdes_cfg->linkSpeed_GHz== 6.25f)
	{
		linkSpeed= CSL_SERDES_LINK_RATE_6p25G;
		laneRateScale= CSL_SERDES_LANE_FULL_RATE;
	}
	else if(serdes_cfg->linkSpeed_GHz== 10.f)
	{
		linkSpeed= CSL_SERDES_LINK_RATE_10G;
		laneRateScale= CSL_SERDES_LANE_FULL_RATE;
	}
	else if(serdes_cfg->linkSpeed_GHz== 12.5f)
	{
		linkSpeed= CSL_SERDES_LINK_RATE_12p5G;
		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]);
#if 0
	//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);
#endif
	//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);

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

}

/*soft shutdown and reset HyperLink*/
void K2_HyperLink_soft_reset(Uint32 linkNum)
{
	int i;
	CSL_VusrRegs * hyperLinkRegs= gpHyperLinkRegs[linkNum];

	printf("soft reset HyperLink %d\n", linkNum);
	
	/*disable all portal or remote register operation
	This bit should be set before iloop or reset bits are changed.*/
	hyperLinkRegs->CTL |= CSL_VUSR_CTL_SERIAL_STOP_MASK;

	/*Wait until no Remote Pending Request*/
	while(hyperLinkRegs->STS&CSL_VUSR_STS_RPEND_MASK);

	/*Reset*/
	hyperLinkRegs->CTL |= CSL_VUSR_CTL_RESET_MASK;

	/*wait for a while*/
	for(i=0;i<100;i++)asm(" NOP 5 ");

#ifdef CSL_PSC_PD_HYPERLINK_1
	if(0==linkNum)
	{
		//disable HyperLink through PSC
		KeyStone_disable_PSC_module(CSL_PSC_PD_HYPERLINK_0, CSL_PSC_LPSC_HYPERLINK_0);
		KeyStone_disable_PSC_Power_Domain(CSL_PSC_PD_HYPERLINK_0);
	}
	else
	{
		//disable HyperLink through PSC
		KeyStone_disable_PSC_module(CSL_PSC_PD_HYPERLINK_1, CSL_PSC_LPSC_HYPERLINK_1);
		KeyStone_disable_PSC_Power_Domain(CSL_PSC_PD_HYPERLINK_1);
	}
#else 	//for K2E
	//disable HyperLink through PSC
	KeyStone_disable_PSC_module(CSL_PSC_PD_HYPERLINK, CSL_PSC_LPSC_HYPERLINK);
	KeyStone_disable_PSC_Power_Domain(CSL_PSC_PD_HYPERLINK);
#endif	
}

void K2_HyperLink_Addr_Map(CSL_VusrRegs * hyperLinkRegs, 
	HyperLink_Address_Map * addr_map)
{
	int i;
	
	hyperLinkRegs->TX_SEL_CTL = 
		(addr_map->tx_addr_mask<<CSL_VUSR_TX_SEL_CTL_TXIGNMSK_SHIFT)
		|(addr_map->tx_priv_id_ovl<<CSL_VUSR_TX_SEL_CTL_TXPRIVIDOVL_SHIFT);
		
	hyperLinkRegs->RX_SEL_CTL = 
		(addr_map->rx_seg_sel<<CSL_VUSR_RX_SEL_CTL_RXSEGSEL_SHIFT)
		|(addr_map->rx_priv_id_sel<<CSL_VUSR_RX_SEL_CTL_RXPRIVIDSEL_SHIFT);
		
	for(i= 0; i< 16; i++)
	{
		hyperLinkRegs->RX_PRIV_IDX= i;
		hyperLinkRegs->RX_PRIV_VAL= addr_map->rx_priv_id_map[i];
	}

	for(i= 0; i< 64; i++)
	{
		hyperLinkRegs->RX_SEG_IDX= i;
		hyperLinkRegs->RX_SEG_VAL= 
			addr_map->rx_addr_segs[i].Seg_Base_Addr
			|addr_map->rx_addr_segs[i].Seg_Length;
	}

}

void K2_HyperLink_Interrupt_init(CSL_VusrRegs * hyperLinkRegs, 
	HyperLink_Interrupt_Cfg * int_cfg)
{
	int i;
	
	hyperLinkRegs->CTL = hyperLinkRegs->CTL
		|(int_cfg->int_local<<CSL_VUSR_CTL_INTLOCAL_SHIFT)
		|(int_cfg->sts_int_enable<<CSL_VUSR_CTL_INTENABLE_SHIFT)
		|(int_cfg->sts_int_vec<<CSL_VUSR_CTL_INTVEC_SHIFT)
		|(int_cfg->int2cfg<<CSL_VUSR_CTL_INT2CFG_SHIFT);

	for(i=0; i<64; i++)
	{
		hyperLinkRegs->INT_CTL_IDX = i;
		hyperLinkRegs->INT_CTL_VAL= 
			(int_cfg->int_event_cntl[i].Int_en<<CSL_VUSR_INT_CTL_VAL_INTEN_SHIFT)
			|(int_cfg->int_event_cntl[i].Int_type<<CSL_VUSR_INT_CTL_VAL_INTTYPE_SHIFT)
			|(int_cfg->int_event_cntl[i].Int_pol<<CSL_VUSR_INT_CTL_VAL_INTPOL_SHIFT)
			|(int_cfg->int_event_cntl[i].si_en<<CSL_VUSR_INT_CTL_VAL_SIEN_SHIFT)
			|(int_cfg->int_event_cntl[i].mps<<CSL_VUSR_INT_CTL_VAL_MPS_SHIFT)
			|(int_cfg->int_event_cntl[i].vector<<CSL_VUSR_INT_CTL_VAL_VECTOR_SHIFT);
	}
		
	for(i=0; i<NUM_MPS; i++)
	{
		hyperLinkRegs->INT_PTR_IDX = i;
		hyperLinkRegs->INT_PTR_VAL= int_cfg->int_set_register_pointer[i];
	}

	//clear any pending interrupt
	hyperLinkRegs->INT_CLR= 0xFFFFFFFF;
}

void K2_HyperLink_Init(HyperLink_Config * hyperLink_cfg)
{
	Uint32 uiLinkNum;
	CSL_VusrRegs * hyperLinkRegs;

	uiLinkNum= hyperLink_cfg->link_number;
	if(uiLinkNum >= CSL_HYPERLINK_PER_CNT)
	{
		printf("Link number %d is not supported\n", uiLinkNum);
		return;
	}
		
	hyperLinkRegs= gpHyperLinkRegs[uiLinkNum];

#ifdef CSL_PSC_PD_HYPERLINK_1
	if(0==uiLinkNum)
	{
		if ((CSL_PSC_getPowerDomainState(CSL_PSC_PD_HYPERLINK_0) == PSC_PDSTATE_ON) &&
		   		(CSL_PSC_getModuleState (CSL_PSC_LPSC_HYPERLINK_0) == PSC_MODSTATE_ENABLE))
		{
			// Disable HyperLink before reconfiguring
			K2_HyperLink_soft_reset(0);	//soft reset if it is already enabled
		}
		//enable HyperLink power and clock domain
		KeyStone_enable_PSC_module(CSL_PSC_PD_HYPERLINK_0, CSL_PSC_LPSC_HYPERLINK_0);
	}
	else
	{
		if ((CSL_PSC_getPowerDomainState(CSL_PSC_PD_HYPERLINK_1) == PSC_PDSTATE_ON) &&
		   		(CSL_PSC_getModuleState (CSL_PSC_LPSC_HYPERLINK_1) == PSC_MODSTATE_ENABLE))
		{
			// Disable HyperLink before reconfiguring
			K2_HyperLink_soft_reset(1);	//soft reset if it is already enabled
		}
		//enable HyperLink power and clock domain
		KeyStone_enable_PSC_module(CSL_PSC_PD_HYPERLINK_1, CSL_PSC_LPSC_HYPERLINK_1);
	}
#else 	//for K2E
	if ((CSL_PSC_getPowerDomainState(CSL_PSC_PD_HYPERLINK) == PSC_PDSTATE_ON) &&
	   		(CSL_PSC_getModuleState (CSL_PSC_LPSC_HYPERLINK) == PSC_MODSTATE_ENABLE))
	{
		// Disable HyperLink before reconfiguring
		K2_HyperLink_soft_reset(0);	//soft reset if it is already enabled
	}
	//enable HyperLink power and clock domain
	KeyStone_enable_PSC_module(CSL_PSC_PD_HYPERLINK, CSL_PSC_LPSC_HYPERLINK);
#endif

	TSC_delay_ms(5); //wait for power module up complete.

	/*disable all portal or remote register operation
	This bit should be set before iloop or reset bits are changed.*/
	hyperLinkRegs->CTL |= CSL_VUSR_CTL_SERIAL_STOP_MASK;

	/*Reset*/
	hyperLinkRegs->CTL |= CSL_VUSR_CTL_RESET_MASK;

	if(HYPERLINK_SERDES_INTERNAL_LOOPBACK >= hyperLink_cfg->loopback_mode)
	{
		/*Wait until no Remote Pending Request*/
		while(hyperLinkRegs->STS&CSL_VUSR_STS_RPEND_MASK);

		hyperLinkRegs->CTL |= CSL_VUSR_CTL_LOOPBACK_MASK;

		hyperLink_cfg->serdes_cfg.loopBackMode= CSL_SERDES_LOOPBACK_ENABLED;
	}
	else
	{
		hyperLink_cfg->serdes_cfg.loopBackMode= CSL_SERDES_LOOPBACK_DISABLED;
	}

	//force 4 lanes always
	hyperLinkRegs->PWR = 
		(7<<CSL_VUSR_PWR_H2L_SHIFT)
		|(7<<CSL_VUSR_PWR_L2H_SHIFT)
		|(1<<CSL_VUSR_PWR_PWC_SHIFT)
		|(1<<CSL_VUSR_PWR_QUADLANE_SHIFT)
		|(0<<CSL_VUSR_PWR_ZEROLANE_SHIFT)
		|(0<<CSL_VUSR_PWR_SINGLELANE_SHIFT);

    K2_HyperLink_Addr_Map(hyperLinkRegs, &hyperLink_cfg->address_map);

	/*tell all receivers to ignore close to the first 3uS of data at beginning of training sequence*/
	hyperLinkRegs->SERDES_CTL_STS1= 0xFFFF0000;

	K2_HyperLink_Serdes_Init(uiLinkNum, &hyperLink_cfg->serdes_cfg);

	/*enable operation*/
	hyperLinkRegs->CTL &= ~(CSL_VUSR_CTL_SERIAL_STOP_MASK|CSL_VUSR_CTL_RESET_MASK);
	
	//SerDes_sweep(); //for debug only

    K2_HyperLink_Interrupt_init(hyperLinkRegs, &hyperLink_cfg->interrupt_cfg);
}

void K2_HyperLink_wait_link_up(Uint32 uiLinkNum)
{
	CSL_VusrRegs * hyperLinkRegs;

	printf("Waiting for HyperLink%d up...\n", uiLinkNum);

	hyperLinkRegs= gpHyperLinkRegs[uiLinkNum];
	/*---------wait for link status OK-------------*/
	while(hyperLinkRegs->STS&CSL_VUSR_STS_SERIAL_HALT_MASK);
	while(hyperLinkRegs->STS&CSL_VUSR_STS_PLL_UNLOCK_MASK);
	while(0==(hyperLinkRegs->STS&CSL_VUSR_STS_LINK_MASK));
	while(0==(hyperLinkRegs->LINK_STS&CSL_VUSR_LINK_STS_RX_ONE_ID_MASK));

	/*after initialization, change the delay to default value to improve performance*/
	hyperLinkRegs->SERDES_CTL_STS1= 0x092E0000;

}

