/*  ============================================================================
 *   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
 * =============================================================================
 *  Revision History
 *  ===============
 *  Jan 15, 2012 Brighton created
 * =============================================================================
 */

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

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= CPU_CLK_KHZ/6/datFmt->clockSpeedKHz;

			delayBetweenTrans_clocks = datFmt->delayBetweenTrans_ns/
				(1000000000/(CPU_CLK_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/(CPU_CLK_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/(CPU_CLK_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);
	
}

