/***********************************************************************************************//**
 @file         hal_i2c_mast.c
 @date         $Date:$
 @version      $Revision:$

 @brief        I2C Master Support (MSP430F5xx family)
 **************************************************************************************************/

/***************************************************************************************************
 *                                             INCLUDES
 ***************************************************************************************************/

#include <msp430.h>
#include <stdint.h>
#include "ComTypeDefs.h"
//#include BOARD_HEADER
#include "cavalier_board_cfg_rev2.h"
#include "inc/hw_memmap.h"
#include "ucs.h"
#include "hal_i2c_mast.h"


/***************************************************************************************************
 *                                             DEFINES
 ***************************************************************************************************/


/***************************************************************************************************
 *                                             PROTOTYPES
 ***************************************************************************************************/

static Result_t i2c_waitForBit0(volatile uint8_t *reg, uint8_t bitmask);
static Result_t i2c_waitForBit1(volatile uint8_t *reg, uint8_t bitmask);

/***************************************************************************************************
 *                                             PUBLIC API
 ***************************************************************************************************/

//*****************************************************************************
//
//! \brief Initializes the I2C Master block.
//!
//! This function initializes operation of the I2C Master block. Upon
//! successful initialization of the I2C block, this function will have set the
//! bus speed for the master. If the parameter \e dataRate is
//! USCI_B_I2C_SET_DATA_RATE_400KBPS, then the master block will be set up to
//! transfer data at 400 kbps; otherwise, it will be set up to transfer data at
//! 100 kbps.
//!
//! \param baseAddress is the base address of the I2C Master module.
//! \param dataRate is USCI_B_I2C_SET_DATA_RATE_100KBPS or USCI_B_I2C_SET_DATA_RATE_400KBPS
//!
//!
//! \return Result_t
//
//*****************************************************************************
Result_t I2C_Master_init( uint16_t baseAddress, uint32_t dataRate )
{
    uint16_t preScalarValue;

    //Disable the USCI module and clears the other bits of control register
    HWREG8(baseAddress + OFS_UCBxCTL1) = UCSWRST;

    /*
     * Configure as I2C master mode.
     * UCMST = Master mode
     * UCMODE_3 = I2C mode
     * UCSYNC = Synchronous mode
     */
    HWREG8(baseAddress + OFS_UCBxCTL0) = UCMST + UCMODE_3 + UCSYNC;

    //Configure I2C clock source
    HWREG8(baseAddress + OFS_UCBxCTL1) = UCSSEL_2 + UCSWRST;	// Use SMCLK, keep SW reset

    //preScalarValue = (uint16_t)(UCS_getSMCLK() / dataRate);
    preScalarValue = (uint16_t)(HAL_SMCLK_CLOCK_FREQ / dataRate);
    HWREG16(baseAddress + OFS_UCBxBRW) = preScalarValue;

	// Clear SW reset, allow operations
	HWREG8(baseAddress + OFS_UCBxCTL1) &= ~UCSWRST;

	return RESULT_SUCCESS;
}

//*****************************************************************************
//
//! \brief Disable the I2C block
//!
//!
//! \param baseAddress is the base address of the I2C Master module.
//!
//!
//! \return Result_t
//
//*****************************************************************************
Result_t I2C_Master_disable( uint16_t baseAddress )
{
    //Disable the USCI module and clears the other bits of control register
    HWREG8(baseAddress + OFS_UCBxCTL1) = UCSWRST;

	return RESULT_SUCCESS;
}

/**********************************************************************************************//**
 * @brief   Execute I2C Master Transaction
 *
 * This function works in a polling mode,
 *    1. Writes the device slave address to be written to.
 *    2. In a loop, writes all the bytes over I2C
 *    3. Optionally sends restart, addrs & readback data, \
 *    4. Send Stop bit
 *
 *
 * @return  Result_t
 ***************************************************************************************************/
Result_t I2C_Master_transaction( uint16_t baseAddress, uint8_t slaveAddress, uint8_t *wr_data, uint8_t wr_len, uint8_t *rd_data, uint8_t rd_len )
{
	Result_t result = RESULT_SUCCESS;

	//HAL_TURN_ON_DBGPORT1();

    //Set the address of the slave with which the master will communicate.
    HWREG16(baseAddress + OFS_UCBxI2CSA) = slaveAddress;

    // Do Write transaction
    if( wr_len > 0 )
    {
    	// I2C TX, SEND start condition
    	HWREG8(baseAddress + OFS_UCBxCTL1) |= UCTR + UCTXSTT;

    	// Wait for start bit & addrs to finish
    	result = i2c_waitForBit1( &HWREG8(baseAddress + OFS_UCBxIFG), UCTXIFG );

		// Send remaining bytes
		while( (wr_len > 0) && (result == RESULT_SUCCESS) )
		{
			// test slave receiver ack
			result = (UCB0STAT & UCNACKIFG) ? RESULT_FAIL : RESULT_SUCCESS;
			if( result == RESULT_SUCCESS )
			{
				HWREG8(baseAddress + OFS_UCBxTXBUF) = *wr_data;
				result = i2c_waitForBit1( &HWREG8(baseAddress + OFS_UCBxIFG), UCTXIFG );
				wr_data++;
				wr_len--;
			}
		}

		// Always force a stop in the case of problems. (or no readback data)
		if( (result != RESULT_SUCCESS) || (rd_len == 0) )
		{
			HWREG8(baseAddress + OFS_UCBxCTL1) |= UCTXSTP;                      // I2C stop condition
			i2c_waitForBit0(&HWREG8(baseAddress + OFS_UCBxCTL1), UCTXSTP);
		}
    }

    // Now Do optional Read transaction
    if( (rd_len > 0) && (result == RESULT_SUCCESS) )
    {
        //Set USCI in Receive mode
        HWREG8(baseAddress + OFS_UCBxCTL1) &= ~UCTR;
        //Send restart
        HWREG8(baseAddress + OFS_UCBxCTL1) |= UCTXSTT;

    	// Wait for start bit & addrs to finish
    	result = i2c_waitForBit0(&HWREG8(baseAddress + OFS_UCBxCTL1), UCTXSTT);

    	// Check for slave ack response
    	result = (HWREG8(baseAddress + OFS_UCBxSTAT) & UCNACKIFG) ? RESULT_FAIL : RESULT_SUCCESS;

    	while( (rd_len > 1) && (result == RESULT_SUCCESS) )
    	{
    		// wait for byte to be received
			result = i2c_waitForBit1( &HWREG8(baseAddress + OFS_UCBxIFG), UCRXIFG );
    		*rd_data = HWREG8(baseAddress + OFS_UCBxRXBUF);

    		rd_len--;
    		rd_data++;
    	}

    	// Receive last byte - set stop & do NACK
        HWREG8(baseAddress + OFS_UCBxCTL1) |= UCTXSTP;
        //Capture data from receive buffer after setting stop bit due to
        //MSP430 I2C critical timing.
		//*rd_data = HWREG8(baseAddress + OFS_UCBxRXBUF);
	    //Wait for Stop to finish
		i2c_waitForBit0( &HWREG8(baseAddress + OFS_UCBxCTL1), UCTXSTP );
	    //Wait for RX buffer
		i2c_waitForBit1( &HWREG8(baseAddress + OFS_UCBxIFG), UCRXIFG );
		*rd_data = HWREG8(baseAddress + OFS_UCBxRXBUF);
    }
	//HAL_TURN_OFF_DBGPORT1();

	return (result);
}

/***************************************************************************************************
 *                                   LOCAL FUNCTIONS
 ***************************************************************************************************/

/**********************************************************************************************//**
 * @brief   Wait for I2C Reg bit to become =0
 *
 * @return  Result_t - RESULT_FAIL if timeout
 ***************************************************************************************************/
static Result_t i2c_waitForBit0(volatile uint8_t *reg, uint8_t bitmask)
{
	uint16_t timeout = 200;

	while( (*reg) & bitmask )
	{
		if (--timeout == 0)
		{
			return RESULT_FAIL;
		}
	}
	return RESULT_SUCCESS;
}

/**********************************************************************************************//**
 * @brief   Wait for I2C Reg bit to become =1
 *
 * @return  Result_t - RESULT_FAIL if timeout
 ***************************************************************************************************/
static Result_t i2c_waitForBit1(volatile uint8_t *reg, uint8_t bitmask)
{
	uint16_t timeout = 200;

	while( !((*reg) & bitmask) )
	{
		if (--timeout == 0)
		{
			return RESULT_FAIL;
		}
	}
	return RESULT_SUCCESS;
}
