/*  ============================================================================
 *     Copyright (C) 2013 Texas Instruments Incorporated.       *
 *
 *   Use of this software is controlled by the terms and conditions found in the
 *   license agreement under which this software has been supplied.
 *   ===========================================================================
 */
/** ============================================================================
EDMA test for SPI
 * =============================================================================
 *  Revision History
 *  ===============
 *  23-July-2013 Brighton Feng  file created
 *  19-October-2013 Brighton  add more test cases
 * =============================================================================
 */
#include <stdio.h>
#include <csl_edma3.h>

#include "K2_common.h"
#include "SPI_EDMA_Test.h"

/*align to L2 cache line*/
Uint16 spiEdmaRxBuf[SPI_EDMA_TEST_BUF_SIZE/2] __attribute__ ((aligned (128)));
/*align to L2 cache line*/
Uint16 spiEdmaTxBuf[SPI_EDMA_TEST_BUF_SIZE/2] __attribute__ ((aligned (128)));
Uint32 SPIDAT1_EdmaBuf[SPI_EDMA_TEST_BUF_SIZE/2];

SPI_Data_Format EdmaDataFormat =
{
	/*.delayBetweenTrans_ns = */0,
	/*.ShifDirection        = */SPI_MSB_SHIFT_FIRST,
	/*.disable_CS_timing    = */1,
	/*.clockPolarity        = */SPI_CLOCK_LOW_INACTIVE,
	/*.clockPhase           = */0,
	/*.clockSpeedKHz        = */66000,
	/*.wordLength           = */16
};

SPI_Transfer_Param EdmaTransferParam =
{
	0,     /*Chip select number*/
	2,  /*select one of the 4 SPI formats*/
	SPI_CS_NO_LAST_HOLD, /*hold CS between multiple words*/
	FALSE,  /*Enable the delay counter at the end of the current transaction*/
	2   /*number of bytes per SPI word*/
};

Uint32 EDMA_start_CCNT;

//EDMA initialization for SPI transfer
void SPI_EDMA_Init()
{
	int i;
	
    EDMA_init();

	for(i=0; i<CSL_SPI_PER_CNT; i++)
	{
		EDMA_channel_TC_cfg(SPI_EDMA_TX_channels[i].CC_num,
			SPI_EDMA_TX_channels[i].channel_num, 
			SPI_EDMA_TX_channels[i].TC_num);
		EDMA_interrupt_enable(SPI_EDMA_TX_channels[i].CC_num,
			SPI_EDMA_TX_channels[i].channel_num);

		EDMA_channel_TC_cfg(SPI_EDMA_RX_channels[i].CC_num,
			SPI_EDMA_RX_channels[i].channel_num, 
			SPI_EDMA_RX_channels[i].TC_num);
		EDMA_interrupt_enable(SPI_EDMA_RX_channels[i].CC_num,
			SPI_EDMA_RX_channels[i].channel_num);
	}
	//disable EDMA CC error (to ignore event miss exception)
	gpCIC_regs->ENABLE_CLR_INDEX_REG= CSL_CIC2_EDMACC_2_ERRINT;
#ifndef DEVICE_K2L
	gpCIC_regs->ENABLE_CLR_INDEX_REG= CSL_CIC2_EDMACC_3_ERRINT;
#else
	gpCIC_regs->ENABLE_CLR_INDEX_REG= CSL_CIC2_EDMACC_1_ERRINT;
#endif
}

//initialize source and destination buffers
void SPI_EDMA_BuffInit(Uint8* pucSrcBuf, Uint8* pucDstBuf,Uint32 uiByteLength)
{
    Uint32 uiI;

    for(uiI = 0; uiI < uiByteLength; uiI++)
    {
        *pucSrcBuf++ = (uiI&0xFF);
        *pucDstBuf++ = 0;
    }
    CP15_DCacheCleanInvalidateBuff((Uint32)pucSrcBuf, uiByteLength);
    CP15_DCacheCleanInvalidateBuff((Uint32)pucDstBuf, uiByteLength);
}

//check the result of EDMA
void SPI_EDMA_Check(Uint32 SPI_idx, Uint8* pucSrcBuf, Uint8* pucDstBuf, 
	Uint32 uiByteLength)
{   
    Uint32  uiTpccTxNum, uiTpccRxNum;
    Uint32  uiSpiEdmaTxCh;
    Uint32  uiSpiEdmaRxCh;
    Uint32  uiI, uiCycles, uiThroughput;
    Uint8   srcData, dstData;

    uiTpccTxNum = SPI_EDMA_TX_channels[SPI_idx].CC_num;
    uiSpiEdmaTxCh = SPI_EDMA_TX_channels[SPI_idx].channel_num;
    uiTpccRxNum = SPI_EDMA_RX_channels[SPI_idx].CC_num;
    uiSpiEdmaRxCh = SPI_EDMA_RX_channels[SPI_idx].channel_num;


    /* Wait for Tx completion */
    if(uiSpiEdmaTxCh<32)
	    while ((gpEDMA_CC_regs[uiTpccTxNum]->TPCC_IPR&(1<<(uiSpiEdmaTxCh))) ==0);
	else
	    while ((gpEDMA_CC_regs[uiTpccTxNum]->TPCC_IPRH&(1<<(uiSpiEdmaTxCh-32))) ==0);
	//disable event to avoid EDMA event miss error
	EDMA_event_disable(uiTpccTxNum, uiSpiEdmaTxCh);
	__asm__(" DSB");

    /* Wait for Rx completion */
    if(uiSpiEdmaRxCh<32)
	    while ((gpEDMA_CC_regs[uiTpccRxNum]->TPCC_IPR&(1<<(uiSpiEdmaRxCh))) ==0);
	else
	    while ((gpEDMA_CC_regs[uiTpccRxNum]->TPCC_IPRH&(1<<(uiSpiEdmaRxCh-32))) ==0);
	//disable event to avoid EDMA event miss error
	EDMA_event_disable(uiTpccRxNum, uiSpiEdmaRxCh);
	uiCycles= CCNT_count_cycle_from(EDMA_start_CCNT);
	__asm__(" DSB");

    for(uiI = 0; uiI < uiByteLength; uiI++)
    {
    	srcData= *pucSrcBuf++;
    	dstData= *pucDstBuf++;
        if(srcData!= dstData)
        {
        	printf("data error at byte %d, source data = 0x%x, destination data = 0x%x\n",
        		uiI, srcData, dstData);
            return;
        }
    }

    uiThroughput = (unsigned long long)uiByteLength*8*gMain_Core_Speed_Hz/
    	((unsigned long long)uiCycles*1000000);

	printf("SPI%d EDMA loopback test passed. Throughput= %dMbps\n", 
		SPI_idx, uiThroughput);
}

void SPI_EDMA_test(Uint32 SPI_idx)
{
	Uint32 numDataWord= SPI_EDMA_TEST_BUF_SIZE/EdmaTransferParam.byteOfWord;

	if(CSL_SPI_PER_CNT<=SPI_idx)
	{
		puts("Invalid SPI port number!");
		return;
	}

    SPI_EDMA_BuffInit((Uint8*)spiEdmaTxBuf,(Uint8*)spiEdmaRxBuf,
    	sizeof(spiEdmaRxBuf));   

	//for this test, EDMA write to SPIDAT1 of PORT0 and SPIDAT0 of other ports
	if(CSL_SPI_0==SPI_idx)
	{
		KeyStone_SPIDAT1_buffer_construct((Uint8*)spiEdmaTxBuf, 
			SPIDAT1_EdmaBuf, numDataWord, &EdmaTransferParam);

		EDMA_start_CCNT= CP15_read_CCNT();
		K2_SPI_EDMA_TxRx(SPI_idx, (Uint8*)SPIDAT1_EdmaBuf, (Uint8*)spiEdmaRxBuf, 
			numDataWord, EdmaTransferParam.byteOfWord, EDMA_TO_SPIDAT1_4_bytes);
	}
	else
	{
		KeyStone_set_SPIDAT1_format(SPI_idx, &EdmaTransferParam);

		EDMA_start_CCNT= CP15_read_CCNT();
		K2_SPI_EDMA_TxRx(SPI_idx, (Uint8*)spiEdmaTxBuf, (Uint8*)spiEdmaRxBuf, 
			numDataWord, EdmaTransferParam.byteOfWord, EDMA_TO_SPIDAT0_2_bytes);
	}

    SPI_EDMA_Check(SPI_idx, (Uint8*)spiEdmaTxBuf,(Uint8*)spiEdmaRxBuf,
    	sizeof(spiEdmaRxBuf));

}

