/*  ============================================================================
 *   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 for I2C initialization and read/write driver on KeyStone DSP
 * =============================================================================
 *  Revision History
 *  ===============
 *  Jan 9, 2012 Brighton  file created
 * =============================================================================*/

#include <stdio.h>
#include <string.h>
#include "KeyStone_common.h"
#include "KeyStone_I2C_init_drv.h"

CSL_I2cRegs * i2cRegs = (CSL_I2cRegs *)CSL_I2C_DATA_CONTROL_REGS;

/*I2C output clock <= 400 KHz*/
Uint32 I2C_speed_KHz= 400;

/*Initialize I2C as master*/
void I2C_Master_Init(Uint32 i2c_speed_KHz)
{
	if(i2c_speed_KHz>400)
	{
		puts("ERROR: I2C speed can not be higher than 400KHz!");
		return;
	}

	I2C_speed_KHz= i2c_speed_KHz;

	/*I2C internal input clock is (DSP core clock)/6,
	it should be Prescale to 7~12MHz for I2C internal working clock*/
	i2cRegs->ICPSC= (DSP_Core_Clock_KHz/6/I2C_MODULE_FREQ_KHZ)-1;

	/*I2C output clock <= 400 KHz*/
	i2cRegs->ICCLKL= (I2C_MODULE_FREQ_KHZ/I2C_speed_KHz)/2-6;
	i2cRegs->ICCLKH= (I2C_MODULE_FREQ_KHZ/I2C_speed_KHz)/2-6;

	/*Master mode. The I2C is a master and generates the serial clock on the SCL pin.*/
	i2cRegs->ICMDR= i2cRegs->ICMDR|
		(1<<CSL_I2C_ICMDR_MST_SHIFT)|
		(1<<CSL_I2C_ICMDR_FREE_SHIFT);

	/*Take I2C controller out of reset: 
	enable I2C controller (set IRS bit = 1 in ICMDR).*/
	i2cRegs->ICMDR= i2cRegs->ICMDR|
		(1<<CSL_I2C_ICMDR_IRS_SHIFT);

	TSC_init(); 	//enable TSC for timeout count
	
}

/*I2C read/write operations can not re-enter, so before any I2C operation, 
this function should be called to block other I2C operations*/
void I2C_block()
{
	; 	//to be done
}

/*after complete an I2C operation, free I2C for other operations*/
void I2C_free()
{
	; 	//to be done
}

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

	preTSC= TSCL;
	theoryCycleForOneByte= (DSP_Core_Clock_KHz/I2C_speed_KHz)*9;

	flag= i2cRegs->ICSTR&flag_mask;

	/*Wait until I2C 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> 100*theoryCycleForOneByte)
		{
			printf("I2C_wait_flag 0x%x timeout at device 0x%x, ICSTR=0x%x\n", 
				flag_mask, i2cRegs->ICSAR, i2cRegs->ICSTR);

			/*reset and reinitialize I2C when timeout happens*/
			i2cRegs->ICMDR= 0;
			I2C_Master_Init(I2C_speed_KHz);

			return 0;
		}
		flag= i2cRegs->ICSTR&flag_mask;

	};

	return 1;
}

/*read "uiByteCount" data from I2C device with "slaveAddress",
data save in buffer pointed by "ucBuffer".
if "wait", polling until data trasfer complete, otherwise, let interrupt
handle the data.
return number of bytes received.*/
Uint32 I2C_read(Uint32 slaveAddress, Uint32 uiByteCount, 
	Uint8 * ucBuffer, I2C_Wait wait)
{
	int i;

	/*I2C read/write operations can not re-enter, so before any I2C operation, 
	this function should be called to block other I2C operations*/
	I2C_block();
	
	/*Place I2C in reset (clear IRS = 0 in ICMDR)*/
	i2cRegs->ICMDR= 0;
	
	/*Configure I2C as Master (MST = 1)
	Data Receiver (TRX = 0)*/
	i2cRegs->ICMDR= 
		(1<<CSL_I2C_ICMDR_FREE_SHIFT)|
		(1<<CSL_I2C_ICMDR_MST_SHIFT);
		
	i2cRegs->ICCNT= uiByteCount;
	i2cRegs->ICSAR= slaveAddress;

	/*Make sure the interrupt status register (ICSTR) is cleared*/
	/*Read ICSTR and write it back (write 1 to clear) ICSTR = ICSTR*/
	i2cRegs->ICSTR= i2cRegs->ICSTR;
	/*Read ICIVR until it is zero*/
	while(i2cRegs->ICIVR);

	/*Take I2C controller out of reset: enable I2C controller (set IRS bit = 1 in ICMDR).*/
	i2cRegs->ICMDR= i2cRegs->ICMDR| 
		(1<<CSL_I2C_ICMDR_IRS_SHIFT);

	/*Wait until bus busy bit is cleared (BB = 0 in ICSTR).*/
	if(0==I2C_wait_flag(CSL_I2C_ICSTR_BB_MASK, 0))
	{
		/*after complete an I2C operation, free I2C for other operations*/
		I2C_free(); 
		return 0;
	}	
	/*Generate a START event(set STT = 1 in ICMDR).*/
	/*End transfer/release bus when transfer is done. 
	Generate a STOP event (set STP = 1 in ICMDR).*/
	i2cRegs->ICMDR= i2cRegs->ICMDR| 
		(1<<CSL_I2C_ICMDR_STT_SHIFT)|
		(1<<CSL_I2C_ICMDR_STP_SHIFT);

	if(I2C_NOWAIT==wait)
	{
		/*exits after programmation of the control registers,
		interrupt service routine should be used to handle the data.*/
		return 0;
	}

	for(i= 0; i< uiByteCount; i++)
	{
		
		/*Wait until data is received (ICRRDY = 1 in ICSTR).*/
		if(0==I2C_wait_flag(CSL_I2C_ICSTR_ICRRDY_MASK, CSL_I2C_ICSTR_ICRRDY_MASK))
		{
			/*after complete an I2C operation, free I2C for other operations*/
			I2C_free();
			return 0;
		}	
		
		/*read data from ICDRR.*/
		ucBuffer[i]=i2cRegs->ICDRR;
	}

	/*Wait until bus busy bit is cleared (BB = 0 in ICSTR).*/
	if(0==I2C_wait_flag(CSL_I2C_ICSTR_BB_MASK, 0))
		uiByteCount= 0;

	/*after complete an I2C operation, free I2C for other operations*/
	I2C_free(); 

	return uiByteCount;
}

/*transfer "uiByteCount" data from "ucBuffer" to I2C device with address
"slaveAddress". if "wait", polling until data trasfer complete, otherwise, 
let interrupt handle the data.
return number of bytes transfered.*/
Uint32 I2C_write(Uint32 slaveAddress, Uint32 uiByteCount, 
	Uint8 * ucBuffer, I2C_Wait wait)
{
	int i;

	/*I2C read/write operations can not re-enter, so before any I2C operation, 
	this function should be called to block other I2C operations*/
	I2C_block();
	
	/*Place I2C in reset (clear IRS = 0 in ICMDR)*/
	i2cRegs->ICMDR= 0;
	
	/*Configure I2C as Master (MST = 1)
	Data Receiver (TRX = 0)*/
	i2cRegs->ICMDR= 
		(1<<CSL_I2C_ICMDR_FREE_SHIFT)|
		(1<<CSL_I2C_ICMDR_TRX_SHIFT)|
		(1<<CSL_I2C_ICMDR_MST_SHIFT);
		
	i2cRegs->ICCNT= uiByteCount;
	i2cRegs->ICSAR= slaveAddress;

	/*Make sure the interrupt status register (ICSTR) is cleared*/
	/*Read ICSTR and write it back (write 1 to clear) ICSTR = ICSTR*/
	i2cRegs->ICSTR= i2cRegs->ICSTR;
	/*Read ICIVR until it is zero*/
	while(i2cRegs->ICIVR);

	/*Take I2C controller out of reset: enable I2C controller (set IRS bit = 1 in ICMDR).*/
	i2cRegs->ICMDR= i2cRegs->ICMDR| 
		(1<<CSL_I2C_ICMDR_IRS_SHIFT);

	/*Wait until bus busy bit is cleared (BB = 0 in ICSTR).*/
	if(0==I2C_wait_flag(CSL_I2C_ICSTR_BB_MASK, 0))
	{
		/*after complete an I2C operation, free I2C for other operations*/
		I2C_free(); 	
		return 0;
	}	
	
	/*Generate a START event(set STT = 1 in ICMDR).*/
	/*End transfer/release bus when transfer is done. 
	Generate a STOP event (set STP = 1 in ICMDR).*/
	i2cRegs->ICMDR= i2cRegs->ICMDR| 
		(1<<CSL_I2C_ICMDR_STT_SHIFT)|
		(1<<CSL_I2C_ICMDR_STP_SHIFT);

	if(I2C_NOWAIT==wait)
	{
		/*exits after programmation of the control registers,
		interrupt service routine should be used to handle the data.*/
		return 0;
	}

	for(i= 0; i< uiByteCount; i++)
	{
		/*Wait until transmit is ready (ICXRDY = 1 in ICSTR).*/
		if(0==I2C_wait_flag(CSL_I2C_ICSTR_ICXRDY_MASK, CSL_I2C_ICSTR_ICXRDY_MASK))
		{
			/*after complete an I2C operation, free I2C for other operations*/
			I2C_free(); 
			return 0;
		}	
		
		/*transmit data to ICDXR.*/
		i2cRegs->ICDXR= ucBuffer[i];
	}

	/*Wait until bus busy bit is cleared (BB = 0 in ICSTR).*/
	if(0==I2C_wait_flag(CSL_I2C_ICSTR_BB_MASK, 0))
		uiByteCount= 0;

	/*after complete an I2C operation, free I2C for other operations*/
	I2C_free(); 
	
	return uiByteCount;
}

