/******************************************************************************
** Copyright 2015 MBARI - Monterey Bay Aquarium Research Institute
*******************************************************************************
*******************************************************************************
** Summary  : Routines to control CF2 bifurcated (Tx/Rx) bit-banged I2C I/O
** Filename : io_i2c.c
** Author   : Luke Coletti
** Project  :
** Version  : 1.0
** Compiler : MetroWerks Code Warrior v8.3
** Created  : 12/15/14
** Archived :
*******************************************************************************
** Modification History:
** 01/02/15 : CF2 Port, LJC
*******************************************************************************/

#include        <cfxpico.h>             // Persistor BIOS and I/O Definitions
#include        <io_i2c.h>


extern ushort WrRegVal;
extern ushort WhichRxd;


void I2C_Init(void)
{

	WrRegVal &= ~(SYS_I2C_ENABLE | DURA_I2C_ENABLE);        // set SYS_I2C_ENABLE and DURA_I2C_ENABLE low
	*((vushort *)CS8_BASE_ADDR) = WrRegVal;

	WrRegVal |= (SYS_I2C_SCL | SYS_I2C_TXD);                        // set I2C_CLK and I2C_TXD high
	*((vushort *)CS8_BASE_ADDR) = WrRegVal;

	WhichRxd = 0;

}


int I2C_Select(uchar Select)
{

	Delay1us();

	WrRegVal &= ~(SYS_I2C_ENABLE | DURA_I2C_ENABLE);        // disable both SYS_I2C_ENABLE and DURA_I2C_ENABLE
	*((vushort *)CS8_BASE_ADDR) = WrRegVal;

	Delay1us();

	if( Select == I2C_BUS_DISABLE ) {
		WhichRxd = 0;
		return( I2C_OK );
	}
	else if( Select == I2C_BUS_SYS ) {
		WrRegVal |= SYS_I2C_ENABLE;
		*((vushort *)CS8_BASE_ADDR) = WrRegVal;
		WhichRxd = SYS_I2C_RXD;
	}
	else if( Select == I2C_BUS_DURA ) {
		WrRegVal |= DURA_I2C_ENABLE;
		*((vushort *)CS8_BASE_ADDR) = WrRegVal;
		WhichRxd = DURA_I2C_RXD;
	}
	else
		return( I2C_ERROR );

	Delay1us();

	return( I2C_OK );

}


/****************************************************************************/
/* Assert I2C Start (S) Condition, which is a HIGH to LOW transition of the */
/* I2C_TXD line while I2C_CLK is high. Go out LOW...                        */
/****************************************************************************/
int I2C_Start(void)
{
	ushort sav_mask;


	if( (WhichRxd != SYS_I2C_RXD) && (WhichRxd != DURA_I2C_RXD) )
		return( I2C_ERROR );

	sav_mask = CPUReadInterruptMask();
	CPUWriteInterruptMask(IEV_MASK_ALL);

	WrRegVal |= ( SYS_I2C_TXD | SYS_I2C_SCL);       // set I2C_TXD and I2C_CLK high
	*((vushort *)CS8_BASE_ADDR) = WrRegVal;

	WrRegVal &= ~SYS_I2C_TXD;                                       // set I2C_TXD low
	*((vushort *)CS8_BASE_ADDR) = WrRegVal;

	WrRegVal &= ~SYS_I2C_SCL;                                       // set I2C_CLK low
	*((vushort *)CS8_BASE_ADDR) = WrRegVal;

	CPUWriteInterruptMask(sav_mask);

	return( I2C_OK );

}


/****************************************************************************/
/* Assert I2C Stop (P) Condition, which is a LOW TO HIGH transition of the  */
/* I2C_TXD line while I2C_CLK is high. Go out HIGH...                       */
/****************************************************************************/
void I2C_Stop(void)
{
	ushort sav_mask;


	sav_mask = CPUReadInterruptMask();
	CPUWriteInterruptMask(IEV_MASK_ALL);

	WrRegVal &= ~SYS_I2C_SCL;                                       // set I2C_CLK low
	*((vushort *)CS8_BASE_ADDR) = WrRegVal;

	WrRegVal &= ~SYS_I2C_TXD;                                       // set I2C_TXD low
	*((vushort *)CS8_BASE_ADDR) = WrRegVal;

	WrRegVal |= SYS_I2C_SCL;                                        // set I2C_CLK high
	*((vushort *)CS8_BASE_ADDR) = WrRegVal;

	WrRegVal |= SYS_I2C_TXD;                                        // set I2C_TXD high
	*((vushort *)CS8_BASE_ADDR) = WrRegVal;

	CPUWriteInterruptMask(sav_mask);

}


/****************************************************************************/
/* Write a byte of data. Data is updated only in the I2C data Change (C)    */
/* Condition, i.e., while I2C_CLK is low. Data is valid and must be held    */
/* in the I2C Data (D) Condition, i.e, when I2C_CLK is high. ACK will be    */
/* asserted after the falling edge of SCL on the eighth bit.                */
/****************************************************************************/
int I2C_WrByte(uchar Data)
{
	register int i;
	uchar ret_value;
	ushort sav_mask;


	sav_mask = CPUReadInterruptMask();
	CPUWriteInterruptMask(IEV_MASK_ALL);

	for( i=7; i>=0; i-- ) {

		WrRegVal &= ~SYS_I2C_SCL;                                               // set I2C_CLK low
		*((vushort *)CS8_BASE_ADDR) = WrRegVal;

		if( Data & 0x80 ) {                                                     // test MSB
			WrRegVal |= SYS_I2C_TXD;                                        // set I2C_TXD high
			*((vushort *)CS8_BASE_ADDR) = WrRegVal;
		}
		else{
			WrRegVal &= ~SYS_I2C_TXD;                                       // set I2C_TXD low
			*((vushort *)CS8_BASE_ADDR) = WrRegVal;
		}

		WrRegVal |= SYS_I2C_SCL;                                                // set I2C_CLK high
		*((vushort *)CS8_BASE_ADDR) = WrRegVal;

		Data <<= 1;

	}  // end for

/****************************************************************************/
/* Verify I2C Acknowledge (ACK) Condition, which is a HIGH to LOW transition*/
/* of the I2C_RXD line asserted by the SLAVE on the falling edge of bit 8.  */
/****************************************************************************/

	WrRegVal &= ~SYS_I2C_SCL;                                                               // set I2C_CLK low after eighth data bit
	*((vushort *)CS8_BASE_ADDR) = WrRegVal;

	WrRegVal |= SYS_I2C_TXD;                                                                // set I2C_TXD high for ACK
	*((vushort *)CS8_BASE_ADDR) = WrRegVal;                             // I2C_TXD will be high on exit

	WrRegVal |= SYS_I2C_SCL;                                                                // set I2C_CLK high for ACK
	*((vushort *)CS8_BASE_ADDR) = WrRegVal;

	ret_value = I2C_NAK;

	for( i=0; i<32; i++ ) {
		Data = (ushort) *((vushort *)CS10_BASE_ADDR);   // read memory mapped latch
		if( (Data & WhichRxd) == 0 ) {                                      // check if I2C_RXD is asserted (low)
			ret_value = I2C_ACK;
			break;
		}
	}

	WrRegVal &= ~SYS_I2C_SCL;                                                               // set I2C_CLK low
	*((vushort *)CS8_BASE_ADDR) = WrRegVal;                 // I2C_CLK will be low on exit

	CPUWriteInterruptMask(sav_mask);

	return( ret_value );

}


/****************************************************************************/
/* Read a byte of data. Data is updated only in the I2C data Change (C)     */
/* Condition, i.e., while I2C_CLK is low. Data is valid and must be held    */
/* in the I2C Data (D) Condition, i.e, when I2C_CLK is high. MSB first.     */
/* Presumably, the first bit is already waiting to be read on entry.        */
/****************************************************************************/
int I2C_RdByte(uchar *Data)
{
	register int i;
	ushort val;
	ushort sav_mask;


	*Data = 0;

	sav_mask = CPUReadInterruptMask();
	CPUWriteInterruptMask(IEV_MASK_ALL);

	for( i=7; i>=0; i-- ) {

		WrRegVal |= SYS_I2C_SCL;                                                        // set I2C_CLK high
		*((vushort *)CS8_BASE_ADDR) = WrRegVal;

		val = (ushort) *((vushort *)CS10_BASE_ADDR);            // read memory mapped latch

		if( val & WhichRxd )                                                            // test masked I2C_RXD bit
			*Data |= (uchar) ( 1 << i );                            // I2C framing is MSB first

		WrRegVal &= ~SYS_I2C_SCL;                                                       // set I2C_CLK low
		*((vushort *)CS8_BASE_ADDR) = WrRegVal;         // I2C_CLK will be low on exit

	} // end for

	CPUWriteInterruptMask(sav_mask);

	return( I2C_OK );

}


/****************************************************************************/
/* Assert I2C Acknowledge (ACK) Condition, which is a LOW state on the      */
/* I2C_TXD line asserted by the MASTER.                                     */
/****************************************************************************/
void I2C_WrAck(void)
{
	ushort sav_mask;


	sav_mask = CPUReadInterruptMask();
	CPUWriteInterruptMask(IEV_MASK_ALL);

	WrRegVal &= ~SYS_I2C_TXD;                                       // set I2C_TXD low
	*((vushort *)CS8_BASE_ADDR) = WrRegVal;

	WrRegVal |= SYS_I2C_SCL;                                        // set I2C_CLK high
	*((vushort *)CS8_BASE_ADDR) = WrRegVal;

	WrRegVal &= ~SYS_I2C_SCL;                                       // set I2C_CLK low
	*((vushort *)CS8_BASE_ADDR) = WrRegVal;

	WrRegVal |= SYS_I2C_TXD;                                        // set I2C_TXD high
	*((vushort *)CS8_BASE_ADDR) = WrRegVal;

	CPUWriteInterruptMask(sav_mask);

}


/****************************************************************************/
/* Assert I2C Acknowledge (NAK) Condition, which is a HIGH state on the     */
/* I2C_TXD line asserted by the MASTER.                                     */
/****************************************************************************/
void I2C_WrNak(void)
{
	ushort sav_mask;


	sav_mask = CPUReadInterruptMask();
	CPUWriteInterruptMask(IEV_MASK_ALL);

	WrRegVal |= SYS_I2C_TXD;                                        // set I2C_TXD high
	*((vushort *)CS8_BASE_ADDR) = WrRegVal;

	WrRegVal |= SYS_I2C_SCL;                                        // set I2C_CLK high
	*((vushort *)CS8_BASE_ADDR) = WrRegVal;

	WrRegVal &= ~SYS_I2C_SCL;                                       // set I2C_CLK low
	*((vushort *)CS8_BASE_ADDR) = WrRegVal;

	CPUWriteInterruptMask(sav_mask);

}
