/****************************************************************************\
 *     Copyright (C) 2011, 2012, 2013 Texas Instruments Incorporated.       *
 *                           All Rights Reserved                            *
 *                                                                          *
 * GENERAL DISCLAIMER                                                       *
 * -------------------------------------------------------------------      *
 * All software and related documentation is provided "AS IS" and without   *
 * warranty or support of any kind and Texas Instruments expressly disclaims*
 * all other warranties, express or implied, including, but not limited to, *
 * the implied warranties of merchantability and fitness for a particular   *
 * purpose.  Under no circumstances shall Texas Instruments be liable for   *
 * any incidental, special or consequential damages that result from the    *
 * use or inability to use the software or related documentation, even if   *
 * Texas Instruments has been advised of the liability.                     *
 ****************************************************************************
Example to show memory access and interrupt throughput HyperLink
In this example, Device1's memory are mapped to Device0 through HyperLink 
so, Device0 accesses Device 1 through the HyperLink memory window just like 
access other memory space. Internal loopback is also supported in this 
example, for this case, Device0 actually access its own local memory through 
HyperLink memory window.
 ****************************************************************************
 * Written by :                                                             *
 *            Brighton Feng                                                 *
 *            Texas Instruments                                             *
 *            Nov 5, 2011                                                   *
 *  March 6, 2013 Brighton Feng  Update for K2
 ***************************************************************************/
#include <stdio.h>
#include <string.h>
#include <csl_serdes.h>
#include "HyperLink_debug.h"
#include "HyperLink_intc.h"
#include "HyperLink_test.h"
#include "K2_common.h"
#include "common_test.h"
#include "K2_Board_Init.h"
#include "K2_HyperLink_init.h"

//there may be one or two HyperLink interface on some devices
#define TEST_HYPERLINK_0 		1
#if (2==CSL_HYPERLINK_PER_CNT)
//for device does not have Hyperlink 1, following definition must be 0
#define TEST_HYPERLINK_1 		1
#endif

/*HyperLink test speed at GHz*/
#define HYPERLINK_SPEED_GHZ 	5.f

HyperLink_Loopback_Mode loopbackMode[]=
{
	HYPERLINK_INTERNAL_DIGITAL_LOOPBACK, 	//for link 0
	HYPERLINK_INTERNAL_DIGITAL_LOOPBACK 	//for link 1
};

/*the base address of the DDR can be accessed through Hyperlink*/
#define DDRA_SPACE_ACCESSED_BY_HYPERLINK 	0x88000000
#if (2==CSL_DDR3_PER_CNT)
//comment off below line if DDRB is not avalible
#define DDRB_SPACE_ACCESSED_BY_HYPERLINK 	0x68000000
#endif

//HyperLink remote test address
HyperlinkRemoteTestAddress HyplnkRemoteAddr[]=
{
	{//HyperLink 0
		CSL_HYPERLINK_0_SLV_DATA+0x00020000,//LL2_SRC_ADDR;
		CSL_HYPERLINK_0_SLV_DATA+0x00040000,//LL2_DST_ADDR;
		CSL_HYPERLINK_0_SLV_DATA+0x06100000,//MSMC_RAM_SRC_ADDR;
		CSL_HYPERLINK_0_SLV_DATA+0x06140000,//MSMC_RAM_DST_ADDR;
		CSL_HYPERLINK_0_SLV_DATA+0x04200000,//DDRA_SRC_ADDR;
		CSL_HYPERLINK_0_SLV_DATA+0x04300000,//DDRA_DST_ADDR;
#ifdef DDRB_SPACE_ACCESSED_BY_HYPERLINK
		CSL_HYPERLINK_0_SLV_DATA+0x07200000,//DDRB_SRC_ADDR;
		CSL_HYPERLINK_0_SLV_DATA+0x07300000//DDRB_DST_ADDR;
#else
		0,//DDRB_SRC_ADDR;
		0//DDRB_DST_ADDR;
#endif
	}
#ifdef CSL_HYPERLINK_1_SLV_DATA
	,{//HyperLink 1
		CSL_HYPERLINK_1_SLV_DATA+0x00020000,//LL2_SRC_ADDR;
		CSL_HYPERLINK_1_SLV_DATA+0x00040000,//LL2_DST_ADDR;
		CSL_HYPERLINK_1_SLV_DATA+0x06100000,//MSMC_RAM_SRC_ADDR;
		CSL_HYPERLINK_1_SLV_DATA+0x06140000,//MSMC_RAM_DST_ADDR;
		CSL_HYPERLINK_1_SLV_DATA+0x04200000,//DDRA_SRC_ADDR;
		CSL_HYPERLINK_1_SLV_DATA+0x04300000,//DDRA_DST_ADDR;
#ifdef DDRB_SPACE_ACCESSED_BY_HYPERLINK
		CSL_HYPERLINK_1_SLV_DATA+0x07200000,//DDRB_SRC_ADDR;
		CSL_HYPERLINK_1_SLV_DATA+0x07300000//DDRB_DST_ADDR;
#else
		0,//DDRB_SRC_ADDR;
		0//DDRB_DST_ADDR;
#endif
	}
#endif
};

#define HW_EVENT_FOR_INT_TEST 				0

#ifndef CSL_HYPERLINK_1
#define TEST_HYPERLINK_1 		0
#endif

HyperLink_Config hyperLink_cfg[2] __attribute__ ((section (".far.HyperLinkInit")));

//MMU memory ranges tables
MMU_Memory_Map_Range memory_ranges[]=
{
	{//MSMC RAM
    .uiVirtualAddress   = 0x0C000000,
    .ullPhysicalAddress = 0x0C000000,
    .uiByteCnt          = MSMC_RAM_SIZE_BYTES,
    .attribute          = MMU_MEM_ATTR_NORMAL_CACHE_RA_WB_WA,
    .accessPermission   = MMU_MEM_ATTR_RW,
    .exectuePermission  = MMU_MEM_ATTR_X,
    .shareAttr          = MMU_MEM_ATTR_OUTER_SHARE,
    .isGlobal           = MMU_MEM_ATTR_GLOBAL,
    .isSecure           = MMU_MEM_ATTR_SECURE,
	},                     
	{//DDR3A               
    .uiVirtualAddress   = 0x80000000,
    .ullPhysicalAddress = 0x800000000ULL, 	
#if (DDR3A_SIZE_BYTES>0x80000000ULL)
    .uiByteCnt          = 0x80000000, 	
#else
    .uiByteCnt          = DDR3A_SIZE_BYTES, 	
#endif
    .attribute          = MMU_MEM_ATTR_NORMAL_CACHE_RA_WB_WA,
    .accessPermission   = MMU_MEM_ATTR_RW,
    .exectuePermission  = MMU_MEM_ATTR_XN,
    .shareAttr          = MMU_MEM_ATTR_OUTER_SHARE,
    .isGlobal           = MMU_MEM_ATTR_GLOBAL,
    .isSecure           = MMU_MEM_ATTR_SECURE,
	}
#if (2==CSL_DDR3_PER_CNT)
	,{//DDR3B
    .uiVirtualAddress   = 0x60000000,
    .ullPhysicalAddress = 0x60000000, 	
    .uiByteCnt          = 0x20000000,
    .attribute          = MMU_MEM_ATTR_NORMAL_CACHE_RA_WB_WA,
    .accessPermission   = MMU_MEM_ATTR_RW,
    .exectuePermission  = MMU_MEM_ATTR_XN,
    .shareAttr          = MMU_MEM_ATTR_INNER_SHARE,
    .isGlobal           = MMU_MEM_ATTR_GLOBAL,
    .isSecure           = MMU_MEM_ATTR_SECURE,
	}
#endif
	,{//HyperLink 0
    .uiVirtualAddress   = CSL_HYPERLINK_0_SLV_DATA,
    .ullPhysicalAddress = CSL_HYPERLINK_0_SLV_DATA,
    .uiByteCnt          = 0x10000000,
    .attribute          = MMU_MEM_ATTR_DEVICE,
    .accessPermission   = MMU_MEM_ATTR_RW,
    .exectuePermission  = MMU_MEM_ATTR_XN,
    .shareAttr          = MMU_MEM_ATTR_INNER_SHARE,
    .isGlobal           = MMU_MEM_ATTR_GLOBAL,
    .isSecure           = MMU_MEM_ATTR_SECURE,
	}
#ifdef CSL_HYPERLINK_1_SLV_DATA 	
	,{//HyperLin 1
    .uiVirtualAddress   = CSL_HYPERLINK_1_SLV_DATA,
    .ullPhysicalAddress = CSL_HYPERLINK_1_SLV_DATA,
    .uiByteCnt          = 0x08000000,
    .attribute          = MMU_MEM_ATTR_DEVICE,
    .accessPermission   = MMU_MEM_ATTR_RW,
    .exectuePermission  = MMU_MEM_ATTR_XN,
    .shareAttr          = MMU_MEM_ATTR_INNER_SHARE,
    .isGlobal           = MMU_MEM_ATTR_GLOBAL,
    .isSecure           = MMU_MEM_ATTR_SECURE,
	}
#endif
};

MMU_Long_Format_Config mmu_cfg=
{
	.memory_map        = memory_ranges,
	.uiNumMemMapRanges = sizeof(memory_ranges)/sizeof(MMU_Memory_Map_Range),
	.ullpMMU3rdLevelTT = NULL, 	//No level 3 translation table
	.bAlignCheck       = TRUE,
	.tableCacheAttr    = MMU_TAB_ATTR_CACHE_WB_WA,
	.tableShareAttr	   = MMU_MEM_ATTR_INNER_SHARE,
};

extern void HyperLink_CPU_test(HyperlinkRemoteTestAddress* remoteAddr);
extern void HyperLink_edma_test(HyperlinkRemoteTestAddress* remoteAddr);

char * HyperLink_mode_str[]=
{
    "internal digital loopback",
    "serdes internal loopback",
    "external line loopback",
    "serdes external loopback",
    "",
};

void Hyperlink_config(HyperLink_Config * hyperLink_cfg)
{
	int i, iPrvId, iSeg;
	Uint32 uiSegLen, uiLinkNum;
	HyperLink_RxSegLen rxSegLen;

	uiLinkNum= hyperLink_cfg->link_number;

	printf("HyperLink %d %s test at %.3fGHz...\n", 
		uiLinkNum, HyperLink_mode_str[loopbackMode[uiLinkNum]], HYPERLINK_SPEED_GHZ);
	hyperLink_cfg->loopback_mode = loopbackMode[uiLinkNum];

    /*------------------Initialize Hyperlink Serdes-------------------------*/ 
	hyperLink_cfg->serdes_cfg.inputRefClock = HYPERLINK_SERDES_REF_CLK;
	hyperLink_cfg->serdes_cfg.linkSpeed_GHz = HYPERLINK_SPEED_GHZ;
	hyperLink_cfg->serdes_cfg.numLanes= 4;

	/*----------------Initialize Hyperlink address map----------------------*/ 
	/*use 27 bits address for TX (128 MB) */
	hyperLink_cfg->address_map.tx_addr_mask = TX_ADDR_MASK_0x07FFFFFF;
	
	/*overlay PrivID to higher 4 bits of address for TX*/
	hyperLink_cfg->address_map.tx_priv_id_ovl = TX_PRIVID_OVL_ADDR_30_27;

	/*Select higher 4 bits of address as PrivID for RX*/
	hyperLink_cfg->address_map.rx_priv_id_sel = TX_PRIVID_OVL_ADDR_30_27;

	/*use bit 23~28 (4 bits (23~26) MSB address, 2 bits (27~28) 
	remote PriviID) as index to lookup segment/length table*/
	hyperLink_cfg->address_map.rx_seg_sel = RX_SEG_SEL_ADDR_28_23;

	uiSegLen= 0x00800000;
	rxSegLen= RX_SEG_LEN_0x0_0080_0000;

	/*map remote PrviID to loccal ID 14*/
	for(i=0; i< 16; i++)
		hyperLink_cfg->address_map.rx_priv_id_map[i] = 14;

	/*for this test, 27 bits address space (128 MB) of HyperLink is splitted into 16 segments
	segment 0 -> core 0 local memory (same for all remote PrviID)
	segment 1 -> core 1 local memory (same for all remote PrviID)
	segment 2 -> core 2 local memory (same for all remote PrviID)
	......
	segment 8 -> DDR3A (same for all remote PrviID)
	segment 9 -> DDR3A+1*SegLen (same for all remote PrviID)
	segment A -> DDR3A+2*SegLen (same for all remote PrviID)
	segment B -> DDR3A+3*SegLen (same for all remote PrviID)
	segment C -> MSMC_RAM (same for all remote PrviID)
	segment D -> DDR3B+(PrviID[1:0]*3+0)*SegLen
	segment E -> DDR3B+(PrviID[1:0]*3+1)*SegLen
	segment F -> DDR3B+(PrviID[1:0]*3+2)*SegLen

	For HyperLink0, memory map window start at 0x40000000, 
	the memory map on a remote master is:
	0x40000000 	Core 0 LL2
	0x40800000 	Core 1 LL2
	0x41000000 	Core 2 LL2
	......
	0x44000000~0x45FFFFFF 	DDR3A
	0x46000000~0x467FFFFF 	MSMC_RAM
	0x46800000~0x47FFFFFF 	DDR3B (different master may access different section)

	For HyperLink1, memory map window start at 0x28000000, 
	the memory map on a remote master is:
	0x28000000 	Core 0 LL2
	0x28800000 	Core 1 LL2
	0x29000000 	Core 2 LL2
	......
	0x2C000000~0x2CFFFFFF 	DDR3A
	0x2E000000~0x2E7FFFFF 	MSMC_RAM
	0x2E800000~0x2FFFFFFF 	DDR3B (different master may access different section)
	*/

	/*map local memory into the same segments for all PrivID (remote masters)*/
	for(iSeg= 0; iSeg<8; iSeg++)
	for(iPrvId=0; iPrvId<4; iPrvId++)
	{
		hyperLink_cfg->address_map.rx_addr_segs[(iPrvId<<4)|iSeg].Seg_Base_Addr= 
			0x10000000+iSeg*0x01000000+0x00800000;
		hyperLink_cfg->address_map.rx_addr_segs[(iPrvId<<4)|iSeg].Seg_Length= 
			rxSegLen;
	}

	/*map a part of DDR3 into the same segments for all PrvID (remote masters)*/
	for(iSeg= 8; iSeg<0xC; iSeg++)
	for(iPrvId=0; iPrvId<4; iPrvId++)
	{
		hyperLink_cfg->address_map.rx_addr_segs[(iPrvId<<4)|iSeg].Seg_Base_Addr= 
			DDRA_SPACE_ACCESSED_BY_HYPERLINK+(iSeg-8)*uiSegLen;
		hyperLink_cfg->address_map.rx_addr_segs[(iPrvId<<4)|iSeg].Seg_Length= 
			rxSegLen;
	}

	/*map MSMC_RAM into same segement for all PrvID (remote masters)*/
	for(iPrvId=0; iPrvId<4; iPrvId++)
	{
		hyperLink_cfg->address_map.rx_addr_segs[(iPrvId<<4)|0xC].Seg_Base_Addr= 
			0x0C000000;
		hyperLink_cfg->address_map.rx_addr_segs[(iPrvId<<4)|0xC].Seg_Length= 
			rxSegLen;
	}

#ifdef DDRB_SPACE_ACCESSED_BY_HYPERLINK
	/*map different DDR3 sections into the segements 
	of different PrvID (remote masters)*/
	for(iPrvId=0; iPrvId<4; iPrvId++)
	for(iSeg= 0xD; iSeg<=0xF; iSeg++)
	{
		hyperLink_cfg->address_map.rx_addr_segs[(iPrvId<<4)|iSeg].Seg_Base_Addr= 
			DDRB_SPACE_ACCESSED_BY_HYPERLINK+(iPrvId*3+iSeg-0xD)*uiSegLen;
		hyperLink_cfg->address_map.rx_addr_segs[(iPrvId<<4)|iSeg].Seg_Length= 
			rxSegLen;
	}
#endif
    /*------------------Initialize Hyperlink interrupt----------------------*/ 
	/*map Hyperlink error/status interrupt to interrupt vector 0*/
    hyperLink_cfg->interrupt_cfg.sts_int_enable = 1;
    hyperLink_cfg->interrupt_cfg.sts_int_vec= 0;

	/*interrupt to remote Device to interrupt vector 1*/
    hyperLink_cfg->interrupt_cfg.int_event_cntl[HW_EVENT_FOR_INT_TEST].si_en = 1;
    hyperLink_cfg->interrupt_cfg.int_event_cntl[HW_EVENT_FOR_INT_TEST].mps = 0;
    hyperLink_cfg->interrupt_cfg.int_event_cntl[HW_EVENT_FOR_INT_TEST].vector = 1;

	/*generate interrupt packet to remote Device when local interrupt event happens*/
    hyperLink_cfg->interrupt_cfg.int_local= 0;
	/*route interrupt packet from remote Device to interrupt pending register*/
    hyperLink_cfg->interrupt_cfg.int2cfg = 1;

    K2_HyperLink_Init(hyperLink_cfg);

}

/*simple memory test to verfiy basic function of HyperLink */
unsigned int HyperLink_Mem_Test(unsigned int uiStartAddress,
	unsigned int uiTotalByteCount, unsigned int uiFillByteCount)
{
	unsigned int uiFailCount;
	
	uiFailCount = Memory_quick_test(uiStartAddress, uiTotalByteCount, 
		uiFillByteCount, 256);

	if(0==uiFailCount)
		printf("HyperLink memory test passed at address 0x%x\n",
			uiStartAddress);
			
    return uiFailCount; 
}

void HyperLink_integrity_Test(HyperlinkRemoteTestAddress * remoteAddr)
{
	/*Core 1 LL2*/
	HyperLink_Mem_Test(remoteAddr->LL2_DST_ADDR, 0x40000, 0x10000);

	/*DDR*/
	HyperLink_Mem_Test(remoteAddr->DDRA_DST_ADDR, 0x100000, 0x10000);
	if(remoteAddr->DDRB_DST_ADDR)
		HyperLink_Mem_Test(remoteAddr->DDRB_DST_ADDR, 0x100000, 0x10000);

	/*MSMC_RAM*/
	HyperLink_Mem_Test(remoteAddr->MSMC_RAM_DST_ADDR, 0x40000, 0x10000);
}

/*this interrupt test is done in loopback mode,
a hardware event is trigger manually, a interrupt packet is generated and 
loopback to this Device and trigger interrupt to the CPU. The latency 
between trigger and the entry of the ISR are measured*/
void HyperLink_Interrupt_Test(Uint32 linkNum)
{
	CSL_VusrRegs * hyplnkRegs = gpHyperLinkRegs[linkNum];

	Uint32 uiStartCCNT= CP15_read_CCNT();

	HyperLinkIntCCNT= 0; 	//will be updated by ISR

	/*manually trigger the hardware event, which will generate 
	interrupt packet to remote side*/
	hyplnkRegs->SW_INT= HW_EVENT_FOR_INT_TEST;

	/*the interrupt packet is loop back to this Device and trigger 
	interrupt to this CPU, here waiting for the interrupt*/
	while(0==HyperLinkIntCCNT);

	/*the time stamp at the entry of the interrupt is recorded in the ISR*/

	printf("Hyperlink %d interrupt latency is %d cycles\n", 
		linkNum, HyperLinkIntCCNT- uiStartCCNT);
}

void HyperLink_Test(Uint32 linkNum)
{
	if(hyperLink_cfg[linkNum].loopback_mode != HYPERLINK_SERDES_EXTERNAL_LOOPBACK)
	{
		/*integrity test*/
	    HyperLink_integrity_Test(&HyplnkRemoteAddr[linkNum]); 

		/*interrupt test is done in loopback mode*/
		if(hyperLink_cfg[linkNum].loopback_mode <= HYPERLINK_EXTERNAL_LINE_LOOPBACK)
		{
		    HyperLink_Interrupt_Test(linkNum);
		}

		/*performance test*/
		HyperLink_CPU_test(&HyplnkRemoteAddr[linkNum]);

		//while(1) /*continously transfer data over HyperLink for eye-diagram test*/
		HyperLink_edma_test(&HyplnkRemoteAddr[linkNum]);
	}
	
	print_HyperLink_status(linkNum);

	//soft reset HyperLink after the test completes
	//K2_HyperLink_soft_reset(linkNum);	
}

int main()
{
	/*common initialization for internal modules in K2 device 
	enable GIC, memory protection interrupts, EDC for MSMC RAM */
	K2_common_device_init();
	/*initialize GIC interface for CPU, enable IRQ, FIQ, PMU*/
	K2_common_CPU_init();

	//enable exception handling
	KeyStone_Exception_cfg(TRUE);

	//make HyperLink access to MSMC RAM sharable with ARM core
	K2_SMS_MPAX_init(PRIVID_HYPERLINK, MSMC_SHARE);

	//CPU speed= MAIN_PLL_REF_CLK_MHZ*MAIN_PLL_MULTIPLIER/MAIN_PLL_DIVISOR
	KeyStone_main_PLL_init(MAIN_PLL_REF_CLK_MHZ, MAIN_PLL_MULTIPLIER, MAIN_PLL_DIVISOR);

#ifndef DEVICE_K2E 	//K2E only has main PLL for both ARM and DSP cores
	//ARM core speed= ARM_PLL_REF_CLK_MHZ*ARM_PLL_MULTIPLIER/ARM_PLL_DIVISOR
	K2_ARM_PLL_init(ARM_PLL_REF_CLK_MHZ, ARM_PLL_MULTIPLIER, ARM_PLL_DIVISOR);
	//K2_ARM_PLL_init(125, 8, 1);
#endif

	//DDR configuration
	K2_DDR3A_config(NULL, NULL);
#ifdef DDRB_SPACE_ACCESSED_BY_HYPERLINK
	K2_DDR3B_config(NULL, NULL);
#endif

	/*MMU configure for ARM core*/
	MMU_long_format_init(&mmu_cfg);
	CP15_ICacheEnable();
	CP15_DCacheEnable();
	calc_cycle_measure_overhead();

	memset(hyperLink_cfg, 0, sizeof(hyperLink_cfg));

	hyperLink_cfg[0].link_number= 0;
    Hyperlink_config(&hyperLink_cfg[0]);//hyperlink 0 configuration

#ifdef CSL_HYPERLINK_1
	hyperLink_cfg[1].link_number= 1;
    Hyperlink_config(&hyperLink_cfg[1]);//hyperlink 1 configuration
#endif

#if TEST_HYPERLINK_0
    K2_HyperLink_wait_link_up(0);
#endif
#if TEST_HYPERLINK_1
    K2_HyperLink_wait_link_up(1);
#endif

#if TEST_HYPERLINK_0
	if(HYPERLINK_SERDES_EXTERNAL_LOOPBACK == loopbackMode[0])
		K2_SerDes_external_loopback_enable(CSL_HYPERLINK_0_SERDES_CFG_REGS, 4);
#endif
#if TEST_HYPERLINK_1
	if(HYPERLINK_SERDES_EXTERNAL_LOOPBACK == loopbackMode[1])
		K2_SerDes_external_loopback_enable(CSL_HYPERLINK_1_SERDES_CFG_REGS, 4);
#endif

	HyperLink_Interrupts_Init();

	if(0==K2_Get_device_number())
	{
#if TEST_HYPERLINK_0
		HyperLink_Test(CSL_HYPERLINK_0);
#endif
#if TEST_HYPERLINK_1
		HyperLink_Test(CSL_HYPERLINK_1);
#endif
	}
	else
	{//for two devices test, the second device is slave
		printf("standby for access from HyperLink...\n");
		while(1);
	}

	return 0;
}

