/*  ============================================================================
 *   Copyright (c) Texas Instruments Inc 2012
 *
 *   Use of this software is controlled by the terms and conditions found in the
 *   license agreement under which this software has been supplied.
 *   ===========================================================================
 */
/** ============================================================================
 Example of SPI configuration and read/write driver
 * =============================================================================
 *  Revision History
 *  ===============
 *  Feb 19, 2012 Brighton created
 * =============================================================================
 */

#include <stdio.h>
#include "KeyStone_common.h"
#include "KeyStone_SPI_Init_drv.h"
#include <cslr_device.h>

#define SPI_TIMEOUT_CYCLES 	((1000)*8*100)

CSL_SpiRegs * spiRegs= (CSL_SpiRegs *)CSL_SPI_REGS;

void KeyStone_SPI_init(SPI_Config *spiCfg)
{
	int i;
	Uint8 clockPreScale;
	Uint8 delayBetweenTrans_clocks;
	Uint16 C2T_delay_clocks;
	Uint16 T2C_delay_clocks;
	SPI_Data_Format * datFmt;
	SPI_Interrupt_Config * intCfg;

	/*1. Reset the SPI by clearing the RESET bit in the SPI global control register 0
	(SPIGCR0) to 0.*/
	spiRegs->SPIGCR0 = 0;

	/*2. Take the SPI out of reset by setting SPIGCR0.RESET to 1.*/
	spiRegs->SPIGCR0 = 1;

	/*3. Configure the SPI for master mode by configuring the CLKMOD and MASTER
	bits in the SPI global control register 1 (SPIGCR1).*/
	spiRegs->SPIGCR1 = 
		((spiCfg->loopBack<<CSL_SPI_SPIGCR1_LOOPBACK_SHIFT)&CSL_SPI_SPIGCR1_LOOPBACK_MASK)
		|((1<<CSL_SPI_SPIGCR1_CLKMOD_SHIFT)&CSL_SPI_SPIGCR1_CLKMOD_MASK)
		|((1<<CSL_SPI_SPIGCR1_MASTER_SHIFT)&CSL_SPI_SPIGCR1_MASTER_MASK);

	/*4. Configure the SPI for 3-pin or 4-pin with chip select mode by configuring the SPI
	pin control register 0 (SPIPC0).*/
	spiRegs->SPIPC0 = CSL_SPI_SPIPC0_SOMIFUN_MASK
		|CSL_SPI_SPIPC0_SIMOFUN_MASK|CSL_SPI_SPIPC0_CLKFUN_MASK;
	if(spiCfg->number_SPI_pins>3)
		spiRegs->SPIPC0 |= CSL_SPI_SPIPC0_SCS0FUN0_MASK;
	if(spiCfg->number_SPI_pins>4)
		spiRegs->SPIPC0 |= CSL_SPI_SPIPC0_SCS0FUN1_MASK;
	if(spiCfg->number_SPI_pins>5)
		spiRegs->SPIPC0 |= CSL_SPI_SPIPC0_SCS0FUN2_MASK;
	if(spiCfg->number_SPI_pins>6)
		spiRegs->SPIPC0 |= CSL_SPI_SPIPC0_SCS0FUN3_MASK;

	/*6. Configure the SPI data rate, character length, shift direction, phase, polarity and
	other format options using SPIFMTn selected in step 5.*/	
	for(i= 0; i<4; i++)	/*4 possible formats*/
	{
		if(spiCfg->dataFormat[i])
		{
			datFmt= spiCfg->dataFormat[i];
			if(datFmt->clockSpeedKHz > 66000)
			{
				printf("ERROR: SPI format %d speed higher than 66 MHz!\n", i);
				continue;
			}

			/*SPI internal input clock is (DSP core clock)/6,
			it should be Prescale to expected output clock speed*/
			clockPreScale= DSP_Core_Clock_KHz/6/datFmt->clockSpeedKHz;

			/*round up*/
			if(clockPreScale*datFmt->clockSpeedKHz<DSP_Core_Clock_KHz/6)
				clockPreScale++;

			delayBetweenTrans_clocks = datFmt->delayBetweenTrans_ns/
				(1000000000/(DSP_Core_Clock_KHz/6*1000));
			if(delayBetweenTrans_clocks<2)
				delayBetweenTrans_clocks=2;
			if(delayBetweenTrans_clocks > 65)
			{
				puts("ERROR: delay between transmissions > 65*6 DSP core clocks!");
				continue;
			}
			spiRegs->SPIFMT[i]= 
				(((delayBetweenTrans_clocks-2)<<CSL_SPI_SPIFMT_WDELAY_SHIFT)&CSL_SPI_SPIFMT_WDELAY_MASK)
				|((datFmt->ShifDirection<<CSL_SPI_SPIFMT_SHIFTDIR_SHIFT)&CSL_SPI_SPIFMT_SHIFTDIR_MASK)
				|((datFmt->disable_CS_timing<<CSL_SPI_SPIFMT_DISCSTIMERS_SHIFT)&CSL_SPI_SPIFMT_DISCSTIMERS_MASK)
				|((datFmt->clockPolarity<<CSL_SPI_SPIFMT_POLARITY_SHIFT)&CSL_SPI_SPIFMT_POLARITY_MASK)
				|((datFmt->clockPhase<<CSL_SPI_SPIFMT_PHASE_SHIFT)&CSL_SPI_SPIFMT_PHASE_MASK)
				|(((clockPreScale-1)<<CSL_SPI_SPIFMT_PRESCALE_SHIFT)&CSL_SPI_SPIFMT_PRESCALE_MASK)
				|((datFmt->wordLength<<CSL_SPI_SPIFMT_CHARLEN_SHIFT)&CSL_SPI_SPIFMT_CHARLEN_MASK);
		}
	}

	/*The timeing value is calculated as follows:
	tC2TDELAY = (C2TDELAY + 2) กม SPI module clock period
	Note: If C2TDELAY = 0, then tC2TDELAY = 0.*/
	C2T_delay_clocks = spiCfg->C2T_delay_ns/(1000000000/(DSP_Core_Clock_KHz/6*1000));
	if(2==C2T_delay_clocks||3==C2T_delay_clocks)
		C2T_delay_clocks= 1;
	if(C2T_delay_clocks>4)
		C2T_delay_clocks -= 2;
	T2C_delay_clocks = spiCfg->T2C_delay_ns/(1000000000/(DSP_Core_Clock_KHz/6*1000));
	if(2==T2C_delay_clocks||3==T2C_delay_clocks)
		T2C_delay_clocks= 1;
	if(T2C_delay_clocks>4)
		T2C_delay_clocks -= 2;

	if(C2T_delay_clocks > 255)
	{
		C2T_delay_clocks = 255;
		puts("ERROR: Chip-select-active-to-transmit-start-delay > 257*6 DSP core clocks");
	}
	if(T2C_delay_clocks > 255)
	{
		T2C_delay_clocks = 255;
		puts("ERROR: Transmit-end-to-chip-select-inactive-delay > 257*6 DSP core clocks");
	}

	/*7. In master mode, configure the master delay options using the SPI delay register
	(SPIDELAY).*/
	spiRegs->SPIDELAY= (C2T_delay_clocks<<CSL_SPI_SPIDELAY_C2TDELAY_SHIFT)
		|(T2C_delay_clocks<<CSL_SPI_SPIDELAY_T2CDELAY_SHIFT);

	/*the CS_polarity is defined as invert of the register field*/
	spiRegs->SPIDEF = !spiCfg->CS_polarity;

	/*8. Select the error interrupt notifications by configuring the SPI interrupt register
	(SPIINT0) and the SPI interrupt level register (SPILVL).*/
	if(spiCfg->interruptCfg)
	{
		intCfg= spiCfg->interruptCfg;
		spiRegs->SPILVL = 
			((intCfg->TX_INT_map<<CSL_SPI_SPILVL_TXINTLVL_SHIFT)&CSL_SPI_SPILVL_TXINTLVL_MASK)
			|((intCfg->RX_INT_map<<CSL_SPI_SPILVL_RXINTLVL_SHIFT)&CSL_SPI_SPILVL_RXINTLVL_MASK)
			|((intCfg->overrun_INT_map<<CSL_SPI_SPILVL_OVRNINTLVL_SHIFT)&CSL_SPI_SPILVL_OVRNINTLVL_MASK)
			|((intCfg->bitError_INT_map<<CSL_SPI_SPILVL_BITERRLVL_SHIFT)&CSL_SPI_SPILVL_BITERRLVL_MASK);
		
		spiRegs->SPIINT0 =
			((intCfg->TX_interruptEnable<<CSL_SPI_SPIINT0_TXINTENA_SHIFT)&CSL_SPI_SPIINT0_TXINTENA_MASK)
			|((intCfg->RX_interruptEnable<<CSL_SPI_SPIINT0_RXINTENA_SHIFT)&CSL_SPI_SPIINT0_RXINTENA_MASK)
			|((intCfg->overrunInterruptEnable<<CSL_SPI_SPIINT0_OVRNINTENA_SHIFT)&CSL_SPI_SPIINT0_OVRNINTENA_MASK)
			|((intCfg->bitErrorInterruptEnable<<CSL_SPI_SPIINT0_BITERRENA_SHIFT)&CSL_SPI_SPIINT0_BITERRENA_MASK);
	}

	/*9. Enable the SPI communication by setting the SPIGCR1.ENABLE to 1.*/
	spiRegs->SPIGCR1 |= 
		((1<<CSL_SPI_SPIGCR1_ENABLE_SHIFT)&CSL_SPI_SPIGCR1_ENABLE_MASK);

	/*10. Setup and enable the DMA for SPI data handling and then enable the DMA
	servicing for the SPI data requests by setting the SPIINT0.DMAREQEN to 1.*/
	spiRegs->SPIINT0 |=	((spiCfg->DMA_requestEnable<CSL_SPI_SPIINT0_DMAREQEN_SHIFT)&CSL_SPI_SPIINT0_DMAREQEN_MASK);

	TSC_init(); 	/*initialize TSC for timeout count*/
	
}

/*wait a flag in SPIFLG, 
retun 1 when the SPIFLG&flag_mask=expect, return 0 when timeout*/
Int32 KeyStone_SPI_wait_flag(Uint32 flag_mask, Uint32 expect)
{
	Uint32 preTSC;
	Uint32 delay;
	volatile Uint32 flag;

	preTSC= TSCL;

	flag= spiRegs->SPIFLG&flag_mask;

	/*Wait until SPI flag= expect value*/
	while(flag!= expect)
	{
		/*if wait time is much larger than theoretical transfer time of
		a byte, then it is looked as timeout.*/		
		delay= TSC_getDelay(preTSC);
		if(delay> SPI_TIMEOUT_CYCLES)
		{
			printf("KeyStone_SPI_wait_flag 0x%x timeout, SPIFLG=0x%x\n", 
				flag_mask, spiRegs->SPIFLG);

			return 0;
		}
		flag= spiRegs->SPIFLG&flag_mask;

	};

	return 1;
}

/*Since SPI TX and RX shares same clock and control signals, SPI TX and RX 
actually happens at the same time even you only want to TX or RX. But, 
application may only care for TX or RX, or part of the TX and RX words. 
This function only TX "numTxByte" valid data from the "firstTxByte", 
the valid data are from the "txBuf"; 
only RX "numRxByte" valid data from the "firstRxByte", 
the valid data are stored to the "rxBuf".
Return number of successful words*/
Uint32 KeyStone_SPI_TxRx(Uint8 * txBuf, Uint32 firstTxByte, Uint32 numTxByte,
	Uint8 * rxBuf, Uint32 firstRxByte, Uint32 numRxByte, SPI_CS_Hold CS_hold, 
	Bool delay_enable, Uint32 formatSelect, Uint32 CS_select)
{
	Int32 i;
	Int32 length;
	Uint32 txData, rxData;
	Uint32 bitOfWord, byteOfWord, dataFormat, lastDataFormat;

	/*total transfer word length is the max of the lastRxWord and lastTxWord*/
	if((firstTxByte+numTxByte)>(firstRxByte+numRxByte))
		length= (firstTxByte+numTxByte);
	else
		length= (firstRxByte+numRxByte);

	bitOfWord= (spiRegs->SPIFMT[formatSelect]&CSL_SPI_SPIFMT_CHARLEN_MASK)>>
		CSL_SPI_SPIFMT_CHARLEN_SHIFT;
	if(bitOfWord>8)
		byteOfWord= 2;
	else
		byteOfWord= 1;

	/*------data format: higher 16 bit of SPIDAT1------*/
	if(SPI_CS_NO_HOLD== CS_hold)
		dataFormat= SPI_TRANSMIT_FORMAT_MAKE(0, delay_enable, 
			formatSelect, CS_select);
	else
		dataFormat= SPI_TRANSMIT_FORMAT_MAKE(1, delay_enable, 
			formatSelect, CS_select);

	/*treat last word specially for CS hold*/
	if(SPI_CS_ALWAYS_HOLD== CS_hold)
		lastDataFormat= SPI_TRANSMIT_FORMAT_MAKE(1, delay_enable, 
			formatSelect, CS_select);
	else
		lastDataFormat= SPI_TRANSMIT_FORMAT_MAKE(0, delay_enable, 
			formatSelect, CS_select);

	if(length==byteOfWord)
		dataFormat= lastDataFormat;

	/*--------write the first word--------*/
	/* Wait for room in TX buffer */
	if(0==KeyStone_SPI_wait_flag(CSL_SPI_SPIFLG_TXINTFLG_MASK, CSL_SPI_SPIFLG_TXINTFLG_MASK))
		return 0;

	if((firstTxByte<=0)&&(0<firstTxByte+numTxByte))
	{/*only TX valid data from the "firstTxByte"*/
		if(2==byteOfWord)
		{/*two bytes*/
			txData= *(Uint16 *)txBuf;
			txBuf+=2;
		}
		else
		{/*one byte*/
			txData= *txBuf++;
		}
	}
	else
		txData= 0;

	txData |= dataFormat;

	/* Send the word */
	spiRegs->SPIDAT1 = txData;

	/*--------write the word n+1 while read word n--------*/
	for( i=0; i<length-byteOfWord; i+= byteOfWord )
	{
		/* Wait for room in TX buffer */
		if(0==KeyStone_SPI_wait_flag(CSL_SPI_SPIFLG_TXINTFLG_MASK, CSL_SPI_SPIFLG_TXINTFLG_MASK))
			return i+byteOfWord;

		if((firstTxByte<=i+byteOfWord)&&(i+byteOfWord<firstTxByte+numTxByte))
		{/*only TX valid data from the "firstTxByte"*/
			if(2==byteOfWord)
			{/*two bytes*/
				txData= *(Uint16 *)txBuf;
				txBuf+=2;
			}
			else
			{/*one byte*/
				txData= *txBuf++;
			}
		}
		else
			txData= 0;

		if(i+byteOfWord==length-byteOfWord)	/*the last word*/
			txData |= lastDataFormat;
		else
			txData |= dataFormat;

		/* Send the word */
		spiRegs->SPIDAT1 = txData;

		/* Wait for data in RX buffer */
		if(0==KeyStone_SPI_wait_flag(CSL_SPI_SPIFLG_RXINTFLG_MASK, CSL_SPI_SPIFLG_RXINTFLG_MASK))
			return i;

		/* Read the next word */
		rxData = spiRegs->SPIBUF&0xFFFF;
		if((firstRxByte<=i)&&(i<firstRxByte+numRxByte))
		{/*only RX valid data from the "firstRxByte"*/
			if(2==byteOfWord)
			{/*two bytes*/
				*(Uint16 *)rxBuf= rxData;
				rxBuf+=2;
			}
			else
			{/*one byte*/
				*rxBuf++= rxData;
			}
		}
	}

	/*--------read the last word--------*/
	/* Wait for data in RX buffer */
	if(0==KeyStone_SPI_wait_flag(CSL_SPI_SPIFLG_RXINTFLG_MASK, CSL_SPI_SPIFLG_RXINTFLG_MASK))
		return length-byteOfWord;

	/* Read the next word */
	rxData = spiRegs->SPIBUF&0xFFFF;
	if((firstRxByte<=length-byteOfWord)&&(length-byteOfWord<firstRxByte+numRxByte))
	{/*only RX valid data from the "firstRxByte"*/
		if(2==byteOfWord)
		{/*two bytes*/
			*(Uint16 *)rxBuf= rxData;
			rxBuf+=2;
		}
		else
		{/*one byte*/
			*rxBuf++= rxData;
		}
	}
	
	return length;
}

