/*  ============================================================================
 *   Copyright (c) Texas Instruments Inc 2011
 *
 *   Use of this software is controlled by the terms and conditions found in the
 *   license agreement under which this software has been supplied.
* ============================================================================
 Examples to show the usage and performance of SRIO on KeyStone DSP
 these tests are loopback test
* =============================================================================
 *  Revision History
 *  ===============
 *  June 13, 2011 Brighton Feng   File Created
 * ============================================================================
 */
#include <stdio.h>
#include <csl_qm_queue.h>
#include "SRIO_PktDMA_Init.h"
#include "SRIO_test.h"
#include "SRIO_debug.h"

SRIO_Packet_Type packet_type[]=
{
	SRIO_PKT_TYPE_SWRITE   ,
	//SRIO_PKT_TYPE_NWRITE   ,
    //SRIO_PKT_TYPE_NWRITE_R ,
	SRIO_PKT_TYPE_NREAD    ,
	SRIO_PKT_TYPE9_STREAM  , 
	SRIO_PKT_TYPE11_MESSAGE
};

Uint32 directIO_src_dest[][2]=
{
	{((Uint32)&packetBuffer_LL2_Size1[0][0])+0x10000000,	((Uint32)&packetBuffer_LL2_Size1[1][0])+0x10000000}, 	/*LL2->LL2*/
	{((Uint32)&packetBuffer_LL2_Size1[0][0])+0x10000000,	((Uint32)&packetBuffer_SL2_Size1[1][0])}, 				/*LL2->SL2*/
	{((Uint32)&packetBuffer_LL2_Size1[0][0])+0x10000000,	((Uint32)&packetBuffer_DDR_Size1[1][0])}, 				/*LL2->DDR*/
	{((Uint32)&packetBuffer_SL2_Size1[0][0]),				((Uint32)&packetBuffer_LL2_Size1[1][0])+0x10000000}, 	/*SL2->LL2*/
	{((Uint32)&packetBuffer_SL2_Size1[0][0]),				((Uint32)&packetBuffer_SL2_Size1[1][0])}, 				/*SL2->SL2*/
	{((Uint32)&packetBuffer_SL2_Size1[0][0]),				((Uint32)&packetBuffer_DDR_Size1[1][0])}, 				/*SL2->DDR*/
	{((Uint32)&packetBuffer_DDR_Size1[0][0]),				((Uint32)&packetBuffer_LL2_Size1[1][0])+0x10000000}, 	/*DDR->LL2*/
	{((Uint32)&packetBuffer_DDR_Size1[0][0]),				((Uint32)&packetBuffer_SL2_Size1[1][0])}, 				/*DDR->SL2*/
	{((Uint32)&packetBuffer_DDR_Size1[0][0]),				((Uint32)&packetBuffer_DDR_Size1[1][0])}, 				/*DDR->DDR*/
};

Uint32 message_src_dest[][2]=
{
	{CORE0_LL2_HOST_SIZE1_FDQ,	DSP0_SRIO_BASE_ID+SRIO_RX_FLOW_CORE1_LL2}, 	/*LL2->LL2*/
	{CORE0_LL2_HOST_SIZE1_FDQ,	DSP0_SRIO_BASE_ID+SRIO_RX_FLOW_SL2      }, 	/*LL2->SL2*/
	{CORE0_LL2_HOST_SIZE1_FDQ,	DSP0_SRIO_BASE_ID+SRIO_RX_FLOW_DDR      }, 	/*LL2->DDR*/
	{SL2_HOST_SIZE1_FDQ,		DSP0_SRIO_BASE_ID+SRIO_RX_FLOW_CORE1_LL2},    /*SL2->LL2*/
	{SL2_HOST_SIZE1_FDQ,		DSP0_SRIO_BASE_ID+SRIO_RX_FLOW_SL2      }, 	/*SL2->SL2*/
	{SL2_HOST_SIZE1_FDQ,		DSP0_SRIO_BASE_ID+SRIO_RX_FLOW_DDR      }, 	/*SL2->DDR*/
	{DDR_HOST_SIZE1_FDQ,		DSP0_SRIO_BASE_ID+SRIO_RX_FLOW_CORE1_LL2},   /*DDR->LL2*/
	{DDR_HOST_SIZE1_FDQ,		DSP0_SRIO_BASE_ID+SRIO_RX_FLOW_SL2      }, 	/*DDR->SL2*/
	{DDR_HOST_SIZE1_FDQ,		DSP0_SRIO_BASE_ID+SRIO_RX_FLOW_DDR      }, 	/*DDR->DDR*/
};

/*transfer through single SRIO port*/
void srio_signle_port_transfer(SRIO_Transfer_Param * transferParam,
	Uint32 uiPortNum)
{
	SRIO_LSU_Transfer lsuTransfer;
	Uint32 uiCompletionCode;
	Uint32 cycles;
	Uint32 uiQueueNum;
	volatile Uint32 * uipSrc, * uipDst;
	Uint32 uiByteCount, uiWordIndex;
	HostPacketDescriptor * hostDescriptor;
	SRIO_Type9_Message_RX_Descriptor * type9MsgRxDesc;
	SRIO_Type11_Message_RX_Descriptor * type11MsgRxDesc;
	Uint32 uiPortSpeed_Mbps= (Uint32)(serdesLinkSetup.linkSpeed_GHz*1000)*
		(srio_cfg.srio_1x2x4x_path_control>>CSL_SRIO_RIO_PLM_SP_PATH_CTL_PATH_CONFIGURATION_SHIFT);

	uiByteCount = transferParam->byteCount;

	if(SRIO_PKT_TYPE9_STREAM==transferParam->packet_type)
	{
		if(uiByteCount>64*1024)	//maximum TYPE9 transfer size
			return; 

	}
	else if(SRIO_PKT_TYPE11_MESSAGE==transferParam->packet_type)
	{
		if(uiByteCount>4*1024)	//maximum TYPE11 transfer size
			return; 
	}
	else
	{
		if(uiByteCount>1024*1024)	//maximum LSU transfer size
			return; 
	}

	if(transferParam->packet_type<0x90) 	//directIO
	{
		uipSrc = (volatile Uint32 *)transferParam->source;
		uipDst= (volatile Uint32 *)transferParam->dest;

		/*initialize buffer*/
		InitDataBuffer((Uint32 *)uipSrc, (Uint32 *)uipDst, uiByteCount);

		InitLsuTransfer(&lsuTransfer, transferParam, uiPortNum, 
			DSP0_SRIO_BASE_ID+uiPortNum, uiPortNum);
		
		cycles= TSCL;
		KeyStone_SRIO_LSU_transfer(&lsuTransfer);

		uiCompletionCode= KeyStone_SRIO_wait_LSU_completion(uiPortNum,
			lsuTransfer.transactionID, lsuTransfer.contextBit);

		if(0==uiCompletionCode)	//complete without error
		{
			/*wait for theoretical transfer time*/
			while(TSC_getDelay(cycles)<(uiByteCount*10*1000/uiPortSpeed_Mbps)+300);

			/*Since NWRITE/SWRITE complete without response, 
			we need waiting for last data landing*/
			uiWordIndex= uiByteCount/4-1;
			while(uipSrc[uiWordIndex]!=uipDst[uiWordIndex])
			{	
				InvalidCache((void *)&uipDst[uiWordIndex], 4);
				if(TSC_getDelay(cycles)>
					(uiByteCount*10*1000/uiPortSpeed_Mbps)*4+4000) 	//timeout
				{
					break;
				}
			};
		}
		cycles= TSC_count_cycle_from(cycles);

	}
	else 	//message
	{
		hostDescriptor= (HostPacketDescriptor *)
			KeyStone_queuePop(transferParam->source);
		if(NULL==hostDescriptor)
		{
			printf("Source queue %d descriptor is NULL\n", transferParam->source);
			SRIO_Check_Free_Queues(); 	//for debug
			return;
		}
	
		/*invalid cache before read descriptor RAM*/
		InvalidCache((void *)hostDescriptor, 64);

		/*initialize the source buffer*/
		uipSrc= (volatile Uint32 *)hostDescriptor->buffer_ptr;
		InitDataBuffer((Uint32 *)uipSrc, NULL, uiByteCount);
	
		if(SRIO_PKT_TYPE9_STREAM==transferParam->packet_type)
		{
			KeyStone_SRIO_Build_Type9_Msg_Desc(hostDescriptor,
				DSP0_SRIO_BASE_ID, transferParam->dest, uiByteCount, 0xAAAA, 0x55);
		}
		else if(SRIO_PKT_TYPE11_MESSAGE==transferParam->packet_type)
		{
			KeyStone_SRIO_Build_Type11_Msg_Desc(hostDescriptor,
				DSP0_SRIO_BASE_ID, transferParam->dest, uiByteCount, 0, 0);
		}
		else
		{
			printf("Invalid packet type %d\n", transferParam->packet_type);
			return;
		}
		/*the SRC_TAG_LO field in the Tx descriptor is used as RX flow ID*/
		hostDescriptor->src_tag_lo= 
			(transferParam->dest- DSP0_SRIO_BASE_ID);//for PktDMA loopback test

		/*write back data from cache to descriptor RAM*/
		WritebackCache((void *)hostDescriptor, 64);

		cycles= TSCL;
		/*push the packet descriptor to Packet DMA TX queue*/
		KeyStone_queuePush(QMSS_SRIO_QUEUE_BASE+uiPortNum, 
			(Uint32)hostDescriptor|FETCH_SIZE_64);
		_mfence(); 	//force all memory operation complete
		_mfence(); 	//force all memory operation complete
		
		/*dest flow = destID - BASE_ID*/
		uiQueueNum= SRIO_RX_FLOW0_DST_Q+ (transferParam->dest- DSP0_SRIO_BASE_ID);
		/*poll the packet descriptor in destination queue*/	
		while(0==KeyStone_GetQueueEntryCount(uiQueueNum))
		{
			if(TSC_getDelay(cycles)>0x3FFFFFFF) 	//timeout
			{
				printf("timeout for packet at 0x%x\n", (Uint32)uipSrc);
				SRIO_Check_TxRx_Queues(); 	//for debug
				SRIO_Check_Free_Queues(); 	//for debug
				return;
			}
		}
		cycles= TSC_count_cycle_from(cycles);

		/*pop descriptor from destination queue*/
		hostDescriptor= (HostPacketDescriptor *)
			KeyStone_queuePop(uiQueueNum);

		/*invalid cache before read descriptor RAM*/
		InvalidCache((void *)hostDescriptor, 64);

		/*get the destination buffer*/
		uipDst= (volatile Uint32 *)hostDescriptor->buffer_ptr;

		if(hostDescriptor->packet_length != uiByteCount)
		{
			printf("received byte count %d != sent byte count %d\n",
				hostDescriptor->packet_length, uiByteCount);
		}
		/*get completion code*/
		if(SRIO_PKT_TYPE9_STREAM==transferParam->packet_type)
		{
			type9MsgRxDesc= (SRIO_Type9_Message_RX_Descriptor *)
				(((Uint32)hostDescriptor)+32);
			uiCompletionCode= type9MsgRxDesc->CC;
			if(type9MsgRxDesc->Dest_ID!= transferParam->dest)
			{
				printf("received destination ID 0x%x != 0x%x\n",
					type9MsgRxDesc->Dest_ID, transferParam->dest);
			}
		}
		else if(SRIO_PKT_TYPE11_MESSAGE==transferParam->packet_type)
		{
			type11MsgRxDesc= (SRIO_Type11_Message_RX_Descriptor *)
				(((Uint32)hostDescriptor)+32);
			uiCompletionCode= type11MsgRxDesc->CC;
		}

		/*descriptor Reclamation*/
#if 0
		uiQueueNum= (hostDescriptor->pkt_return_qmgr<<12)|
			hostDescriptor->pkt_return_qnum;
		KeyStone_queuePush(uiQueueNum, (Uint32)hostDescriptor|FETCH_SIZE_64);
#else
		if(hostDescriptor->next_desc_ptr)
		{
			HostPacketDescriptor * nextHostDescriptor= 
				(HostPacketDescriptor *)hostDescriptor->next_desc_ptr;
		
			hostDescriptor->next_desc_ptr= NULL;
			
			/*write back data from cache to descriptor RAM*/
			WritebackCache((void *)hostDescriptor, 64);

			/*invalid cache before read descriptor RAM*/
			InvalidCache((void *)nextHostDescriptor, 64);

			uiQueueNum= (nextHostDescriptor->pkt_return_qmgr<<12)|
				nextHostDescriptor->pkt_return_qnum;
			KeyStone_queuePush(uiQueueNum, (Uint32)nextHostDescriptor|FETCH_SIZE_64);			
		}

		uiQueueNum= (hostDescriptor->pkt_return_qmgr<<12)|
			hostDescriptor->pkt_return_qnum;
		KeyStone_queuePush(uiQueueNum, (Uint32)hostDescriptor|FETCH_SIZE_64);
#endif
		_mfence(); 	//force all memory operation complete
		_mfence(); 	//force all memory operation complete
		
	}

	printf("%s from 0x%8x to 0x%8x, %6d bytes, %6d cycles, %5lld Mbps, completion code = %d\n", 
		get_packet_type_string(transferParam->packet_type),
		uipSrc, uipDst, uiByteCount, cycles,
		(unsigned long long)uiByteCount*8*gDSP_Core_Speed_Hz/cycles/1000000, 
		uiCompletionCode);

	VerifyData((Uint32 *)uipSrc, (Uint32 *)uipDst, uiByteCount);

	if(transferParam->packet_type>=0x90) 	//message
	{
		SRIO_Check_TxRx_Queues(); 	//for debug
		SRIO_Check_Free_Queues(); 	//for debug
	}
}

/*test SRIO transfer through single port*/
void srio_signle_port_loopback_test(Uint32 uiPortNum, 
	SRIO_Loopback_Mode loopback_mode)
{
	int k, m, n;
	SRIO_Transfer_Param testParam;
	Uint32 uiSrcDstComb;

	//test different packet types
	for(k=0; k< sizeof(packet_type)/sizeof(SRIO_Packet_Type); k++)
	{
		testParam.packet_type= packet_type[k];

		//test different source/destination combinations
		if(testParam.packet_type<0x90) 	//directIO
		{
			uiSrcDstComb= sizeof(directIO_src_dest)/8;
		}
		else 	//message
		{
			if(SRIO_EXTERNAL_FORWARD_BACK==loopback_mode)
				continue; 	//forwarding does not support message
				
			uiSrcDstComb= sizeof(message_src_dest)/8;
		}
		for(m=0; m< uiSrcDstComb; m++)
		{
			if(testParam.packet_type<0x90) 	//directIO
			{
				testParam.source= directIO_src_dest[m][0];
				testParam.dest= directIO_src_dest[m][1];
			}
			else 	//message
			{
				testParam.source= message_src_dest[m][0];
				testParam.dest= message_src_dest[m][1];
			}
			
			//test different packet size
			for(n=8; n<= LL2_PACKET_BUFFER_SIZE1; n*=2)
			{
				    testParam.byteCount = n;                            
				srio_signle_port_transfer(&testParam, uiPortNum);
			}
		}
	}
	//print_SRIO_status_error(&srio_cfg.blockEn);
	puts("");
}

/*transfer through multiple SRIO ports in parallel*/
void srio_multiple_port_transfer(SRIO_Multiple_Test_Config * test_cfg)
{
	int j, k;
	SRIO_Transfer_Param * transferParam;
	SRIO_LSU_Transfer lsuTransfer[4];
	Int32 uiCompletionCode[4]= {-1, -1, -1, -1}, uiCompletion_context_code;
	Uint32 cycles[4];
	Uint32 uiQueueNum, uiWordIndex;
	Uint32 * uipSrc[4], * uipDst[4], uiByteCount[4], uiDestID[4];
	SRIO_Packet_Type packet_type[4];
	HostPacketDescriptor * hostDescriptor[4];
	SRIO_Type9_Message_RX_Descriptor * type9MsgRxDesc;
	SRIO_Type11_Message_RX_Descriptor * type11MsgRxDesc;

	//prepare all transfer requests
	for(j= 0; j< 4; j++)
	{
		transferParam= &test_cfg->transfer_param[j];
		uiByteCount[j]= transferParam->byteCount;
		if(0==uiByteCount[j])
		{
			uiCompletionCode[j]= 0;
			uipSrc[j] = 0;
			uipDst[j]= 0;
			uiDestID[j] = 0;

			continue;
		}

		packet_type[j]= transferParam->packet_type;
		if(packet_type[j]<0x90) 	//directIO
		{
			uipSrc[j] = (Uint32 *)transferParam->source;
			uipDst[j]= (Uint32 *)transferParam->dest;

			/*initialize buffer*/
			InitDataBuffer(uipSrc[j], uipDst[j], uiByteCount[j]);

			InitLsuTransfer(&lsuTransfer[j], transferParam, j,
				DSP0_SRIO_BASE_ID+j, j);
        }
		else 	//message
		{
			hostDescriptor[j]= (HostPacketDescriptor *)
				KeyStone_queuePop(transferParam->source);
			if(NULL==hostDescriptor[j])
			{
				printf("Source queue descriptor %d is NULL\n", 
					transferParam->source);
				SRIO_Check_Free_Queues(); 	//for debug
				return;
			}
		
			/*invalid cache before read descriptor RAM*/
			InvalidCache((void *)hostDescriptor[j], 64);

			/*initialize the source buffer*/
			uipSrc[j]= (Uint32 *)hostDescriptor[j]->buffer_ptr;
			InitDataBuffer(uipSrc[j], NULL, uiByteCount[j]);

			uiDestID[j]= transferParam->dest;
			if(SRIO_PKT_TYPE9_STREAM==packet_type[j])
			{
				KeyStone_SRIO_Build_Type9_Msg_Desc(hostDescriptor[j],
					DSP0_SRIO_BASE_ID, uiDestID[j], uiByteCount[j], 0xAAAA, 0x55);
			}
			else if(SRIO_PKT_TYPE11_MESSAGE==packet_type[j])
			{
				KeyStone_SRIO_Build_Type11_Msg_Desc(hostDescriptor[j],
					DSP0_SRIO_BASE_ID, uiDestID[j], uiByteCount[j], 0, 0);
			}
			else
			{
				printf("Invalid packet type %d\n", packet_type[j]);
				return;
			}

			/*write back data from cache to descriptor RAM*/
			WritebackCache((void *)hostDescriptor[j], 64);
		}
        
    }   

    //submit all transfers
	for(j= 0; j< 4; j++)
	{
		transferParam= &test_cfg->transfer_param[j];
		if(0==uiByteCount[j])
			continue;
		if(packet_type[j]<0x90) 	//directIO
		{

			cycles[j]= TSCL;
			KeyStone_SRIO_LSU_transfer(&lsuTransfer[j]);

		}
		else 	//message
		{
			cycles[j]= TSCL;
			/*push the packet descriptor to Packet DMA TX queue*/
			KeyStone_queuePush(QMSS_SRIO_QUEUE_BASE+j, 
				(Uint32)hostDescriptor[j]|FETCH_SIZE_64);
			_mfence(); 	//force all memory operation complete
			_mfence(); 	//force all memory operation complete
			
		}
	}

	//wait all transfers complete
	while((-1==uiCompletionCode[0])||(-1==uiCompletionCode[1])||
		(-1==uiCompletionCode[2])||(-1==uiCompletionCode[3]))
	{
		for(j= 0; j< 4; j++)
		{
			if(-1!=uiCompletionCode[j])
				continue;

			if(packet_type[j]<0x90) 	//DirectIO
			{
				uiCompletion_context_code= 
					KeyStone_SRIO_get_LSU_completion_context_code(j,
					lsuTransfer[j].transactionID);

				uiWordIndex= uiByteCount[j]/4-1;
				InvalidCache((void *)&uipSrc[j][uiWordIndex], 4);
				InvalidCache((void *)&uipDst[j][uiWordIndex], 4);

				if((uipSrc[j][uiWordIndex]==uipDst[j][uiWordIndex])&&
					((uiCompletion_context_code&1)==lsuTransfer[j].contextBit))
				{
					cycles[j]= TSC_count_cycle_from(cycles[j]);
	                uiCompletionCode[j]= uiCompletion_context_code>>1;
				}
			}
			else	//message
			{
				/*dest flow = destID - BASE_ID*/
				uiQueueNum= SRIO_RX_FLOW0_DST_Q+ 
					(uiDestID[j]- DSP0_SRIO_BASE_ID);
				/*poll the packet descriptor in destination queue*/	
				if(0!=KeyStone_GetQueueEntryCount(uiQueueNum))
				{
					HostPacketDescriptor * descriptor;
					Uint32 * uipDest;

					/*pop descriptor from destination queue*/
					descriptor= (HostPacketDescriptor *)
						KeyStone_queuePop(uiQueueNum);

					/*invalid cache before read descriptor RAM*/
					InvalidCache((void *)descriptor, 64);

					/*get the destination buffer*/
					uipDest= (Uint32 *)descriptor->buffer_ptr;

					/*same destination queue may be used by different port,
					so we need to find corresponding port for this transfer*/
					for(k= 0; k< 4; k++)
					{
						if(NULL==uipSrc[k])
							continue;
						InvalidCache((void *)&uipSrc[k][0], 4);
						InvalidCache((void *)&uipDest[0], 4);
						if(uipDest[0]==uipSrc[k][0])
						{
							cycles[k]= TSC_count_cycle_from(cycles[k]);

							uipDst[k] = uipDest;

							/*get completion code*/
							if(SRIO_PKT_TYPE9_STREAM==packet_type[j])
							{
								type9MsgRxDesc= (SRIO_Type9_Message_RX_Descriptor *)
									(((Uint32)descriptor)+32);
								uiCompletionCode[k]= type9MsgRxDesc->CC;
							}
							else if(SRIO_PKT_TYPE11_MESSAGE==packet_type[j])
							{
								type11MsgRxDesc= (SRIO_Type11_Message_RX_Descriptor *)
									(((Uint32)descriptor)+32);
								uiCompletionCode[k]= type11MsgRxDesc->CC;
							}

							/*descriptor Reclamation*/
							uiQueueNum= (descriptor->pkt_return_qmgr<<12)|
								descriptor->pkt_return_qnum;
							KeyStone_queuePush(uiQueueNum, 
								(Uint32)descriptor|FETCH_SIZE_64);

							break;
						}
					}
				}
			}
		}
	}
	SRIO_Check_TxRx_Queues(); 	//for debug
	SRIO_Check_Free_Queues(); 	//for debug
	
	//check and print result
	for(j= 0; j< 4; j++)
	{
		if(0==uiByteCount[j])
			continue;

		printf("%s from 0x%8x to 0x%8x through port %d, %6d bytes, %6d cycles, %5lld Mbps, completion code = %d\n", 
			get_packet_type_string(packet_type[j]),
			uipSrc[j], uipDst[j], j, uiByteCount[j], cycles[j],
			(unsigned long long)uiByteCount[j]*8*gDSP_Core_Speed_Hz/cycles[j]/1000000, 
			uiCompletionCode[j]);

		VerifyData(uipSrc[j], uipDst[j], uiByteCount[j]);
	}

}

