/*
 * \file   TI_MSPBoot_CI_PHYDL_USCI_I2C_slave.c
 *
 * \brief  Driver for I2C Slave Physical-DataLink layers using USCI
 *
 */
/* --COPYRIGHT--,BSD
 * Copyright (c) 2012, Texas Instruments Incorporated
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * *  Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * *  Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * *  Neither the name of Texas Instruments Incorporated nor the names of
 *    its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * --/COPYRIGHT--*/

//
// Include files
//
#include "msp430.h"
#include "TI_MSPBoot_Common.h"
#include "TI_MSPBoot_CI.h"
#include "TI_MSPBoot_AppMgr.h"

//
//  Configuration checks
//
#ifndef MSPBoot_CI_I2C
#   error "This file uses the I2C interface"
#endif

//
//  Type Definitions
//
/*! State machine used by this communication interface 
 *   USCI doesn't require too much control, so we only check for an idle state
 *   and when receiving a packet 
 */
typedef enum {
    USCI_STATE_IDLE=0,          /*! Initialized state waiting for start */
    USCI_STATE_RECEIVING,       /*! Receiving packet */
}USCI_State_Machine_e;


//
//  Global variables
//
//    Note that these variables are assigned to a specific location in RAM
//    since they can also be used during application.
//    If not used by application, the location doesn't matter.
//
/*! Pointer to the Communication Interface callback structure 
 *   The NWK layer will define its callback structure and pass the pointer to
 *   this layer. An application can also declare its own structure and pass it
 *   to this layer in order to use the same interface.
 *   Note that the declaration for IAR and CCS is different.
 */
#ifdef __IAR_SYSTEMS_ICC__
#pragma location="RAM_CICALLBACK"
__no_init t_CI_Callback  * CI_Callback_ptr;
#elif defined (__TI_COMPILER_VERSION__)
extern t_CI_Callback  * CI_Callback_ptr;
#endif


/*! State machine used by this interface. 
 *   Note that the declaration for IAR and CCS is different.
 */
#ifdef __IAR_SYSTEMS_ICC__
#pragma location="RAM_CISM"
__no_init USCI_State_Machine_e CI_State_Machine;
#elif defined (__TI_COMPILER_VERSION__)
extern USCI_State_Machine_e CI_State_Machine;
#endif


//
//  Local definitions
//
#ifdef CONFIG_CI_PHYDL_I2C_TIMEOUT
/*! Number of clock cycles for a millisecond. Clock runs from SMCLK/4=MCLK/4 */
#define MS_CYCLES                   ((MCLK/4)/1000)
/*! Number of clock cycles for a SMBus timeout (25 ms) */
#define SMBUS_TIMEOUT_CYCLES        (25*MS_CYCLES)
#endif

//
//  Local function prototypes
//
#ifdef CONFIG_CI_PHYDL_I2C_TIMEOUT
static void Timer_Timeout_Init(void);
static void Timer_Timeout_Start(void);
static void Timer_Timeout_Stop(void);
static uint8_t Timer_Timeout_Expired(void);
#endif

//
//  Function declarations
//
/******************************************************************************
*
 * @brief   Initializes the USCI I2C Slave interface
 *  - Sets corresponding GPIOs for I2C functionality
 *  - Resets and then configures the I2C
 *  - Initializes the I2C state machine
 *  - Initializes the I2C callbacks
 *  The NWK layer will define its callback structure and pass the pointer to
 *   this function. An application can also declare its own structure and call
 *   this function in order to use the same interface.
 *
 *  @param  CI_Callback     Pointer to Communication interface callbacks
 *
 * @return  none
 *****************************************************************************/
void TI_MSPBoot_CI_PHYDL_Init(t_CI_Callback * CI_Callback)
{
    P1SEL |= (BIT6 | BIT7);                 // Enable P1[6:7] for USCI_B0 I2C mode
    P1SEL2 |= (BIT6 | BIT7);                //

    UCB0CTL1 |= UCSWRST;                    // Enable SW reset
    UCB0CTL0 = UCMODE_3 + UCSYNC;           // I2C Slave, synchronous mode
    UCB0I2COA = CONFIG_CI_PHYDL_I2C_SLAVE_ADDR;     // set own (slave) address
    UCB0CTL1 &= ~UCSWRST;                   // Clear SW reset, resume operation
#ifdef CONFIG_CI_PHYDL_I2C_TIMEOUT
    Timer_Timeout_Init();
#endif
    // Initialize all callbacks
    CI_Callback_ptr = CI_Callback;
    // Init state machine
    CI_State_Machine = USCI_STATE_IDLE;
}

/******************************************************************************
 *
 * @brief   Disables the USCI module
 *
 * @return  none
 *****************************************************************************/
void TI_MSPBoot_CI_PHYDL_disable(void)
{
    UCB0CTL1 |= UCSWRST;
#ifdef CONFIG_CI_PHYDL_I2C_TIMEOUT
    Timer_Timeout_Stop();
#endif
}

/******************************************************************************
 *
 * @brief   Enables the USCI module
 *
 * @return  none
 *****************************************************************************/
void TI_MSPBoot_CI_PHYDL_reenable(void)
{
    UCB0CTL1 &= ~UCSWRST;
}

/******************************************************************************
*
 * @brief   Polls for USCI flags
 *  - Checks the Start, RX, TX, Stop flags and Timeout timer
 *  - This function calls the corresponding Callback functions and informs 
 *    higher level layers, so they can handle data properly
 *
 * @return  none
 *****************************************************************************/
void TI_MSPBoot_CI_PHYDL_Poll(void)
{
    uint8_t temp;
    uint8_t flag_stat, flag_ifg2;
    
    // Read flags at the beginning of function 
    // New detected flags will be handled in the next poll
    flag_ifg2 = IFG2;
    flag_stat = UCB0STAT;

    if (flag_stat & UCSTTIFG)   // Check for Start bit flag
    {
#ifdef CONFIG_CI_PHYDL_STOP_CALLBACK
        if ((CI_State_Machine != USCI_STATE_IDLE) && 
            (CI_Callback_ptr->StopCallback != NULL))
        {
            // Special case when a stop was cleared by a new Start
            UCB0STAT &= ~UCSTPIFG;              // Clear stop flag
            #ifdef CONFIG_CI_PHYDL_I2C_TIMEOUT
                Timer_Timeout_Stop();           // Stop timeout timer
            #endif
            CI_Callback_ptr->StopCallback();  // Call Stop callback (if valid)
            CI_State_Machine = USCI_STATE_IDLE; // Reset state machine
            return;
        }
#endif            
        // If no stop was expected/used, process the start flag
#ifdef CONFIG_CI_PHYDL_START_CALLBACK
        if (CI_Callback_ptr->StartCallback != NULL)  
        {
            CI_Callback_ptr->StartCallback(); // Call Start callback (if valid)
        }
#endif
        CI_State_Machine = USCI_STATE_RECEIVING;    // Update state machine
        UCB0STAT &= ~UCSTTIFG;                      // Clear start flag
#ifdef CONFIG_CI_PHYDL_I2C_TIMEOUT
        Timer_Timeout_Start();                      // Refresh timer
#endif
    }
    else if (flag_ifg2 & UCB0RXIFG)     // Check the RX flag
    {
        temp = UCB0RXBUF;                           // Get received byte
        if (CI_Callback_ptr->RxCallback != NULL)
        {
            // Call RX Callback (if valid) and send byte to upper layer
            CI_Callback_ptr->RxCallback(temp);      
        }
#ifdef CONFIG_CI_PHYDL_I2C_TIMEOUT
        Timer_Timeout_Start();                      // Refresh timer
#endif
    }
    else if (flag_ifg2 & UCB0TXIFG)     // Check for TX flag
    {
        // Send ACK after byte reception
        if (CI_Callback_ptr->TxCallback != NULL)
        {
            // Call TXCallback (if valid) and get byte to send from upper layer
            CI_Callback_ptr->TxCallback(&temp);     
            UCB0TXBUF = temp;                       // Send byte 
        }
#ifdef CONFIG_CI_PHYDL_I2C_TIMEOUT
        Timer_Timeout_Start();                      // Refresh timer
#endif
    }
    else if (flag_stat & UCSTPIFG)      // Check for Stop bit flag
    {
        UCB0STAT &= ~UCSTPIFG;              
#ifdef CONFIG_CI_PHYDL_I2C_TIMEOUT
        Timer_Timeout_Stop();                       // Stop timeout timer on end of frame
#endif
#ifdef CONFIG_CI_PHYDL_STOP_CALLBACK
        if (CI_Callback_ptr->StopCallback != NULL)
        {
            CI_Callback_ptr->StopCallback();      // Call End callback (if valid)
        }
#endif
        CI_State_Machine = USCI_STATE_IDLE;         // Reset the state machine
    }
#ifdef CONFIG_CI_PHYDL_I2C_TIMEOUT
    else if (Timer_Timeout_Expired() != 0x00)   // Check timeout timer
    {
        Timer_Timeout_Stop();                    
#ifdef CONFIG_CI_PHYDL_ERROR_CALLBACK
        if (CI_Callback_ptr->ErrorCallback != NULL)
        {
            // Call Error callback (if valid) and inform the upper layer
            CI_Callback_ptr->ErrorCallback(1);          
        }
#endif
    }
#endif
}   // TI_MSPBoot_CI_PHYDL_Poll


#ifdef CONFIG_CI_PHYDL_I2C_TIMEOUT
/******************************************************************************
*
 * @brief   Init timeout timer
 *  - Timer will run using SMCLK/4 in UP mode. Counts 25ms and is disabled by 
 *    default
 *
 * @return  none
 *****************************************************************************/
static void Timer_Timeout_Init(void)
{
    // TA1 Uses SMCLK/4 in Up mode and is stopped by default
    TA1CTL = TASSEL_2 + TACLR + MC_0 + ID_2;;
    // Timer will count 25ms
    TA1CCR0 = SMBUS_TIMEOUT_CYCLES;
    // TA1.0 used in compare mode without interrupts
    TA1CCTL0 = 0;;
}

/******************************************************************************
*
 * @brief   Starts timeout timer
 *
 * @return  none
 *****************************************************************************/
static void Timer_Timeout_Start(void)
{
    TA1CTL = TASSEL_2 + TACLR + MC_1 + ID_2;    // Start timer in Up mode
    TA1CCTL0 &= ~CCIFG;                         // Clear flag
}

/******************************************************************************
*
 * @brief   Stop timeout timer
 *
 * @return  none
 *****************************************************************************/
static void Timer_Timeout_Stop(void)
{
    TA1CTL = TASSEL_2 + TACLR + MC_0;   // Stop timer
    TA1CCTL0 &= ~CCIFG;                 // Clear flag
}

/******************************************************************************
*
 * @brief   Checks if timeout timer has expired
 *
 * @return  0 if not expired. !=0 if expired.
 *****************************************************************************/
static uint8_t Timer_Timeout_Expired(void)
{
    return (TA1CCTL0 & CCIFG);          // Check timer flag
}

#endif  //CONFIG_CI_PHYDL_I2C_TIMEOUT

//
//  Constant table
//
/*! Peripheral Interface vectors for Application:
 *  These vectors are shared with application and can be used by the App to
 *   initialize and use the Peripheral interface
 *   Note that the declaration for IAR and CCS is different.
 */
#   ifdef CONFIG_CI_PHYDL_COMM_SHARED
#       ifdef __IAR_SYSTEMS_ICC__
#           pragma location="BOOT_APP_VECTORS"
__root const uint16_t Boot2App_Vector_Table[] =
#       elif defined (__TI_COMPILER_VERSION__)
#           pragma DATA_SECTION(Boot2App_Vector_Table, ".BOOT_APP_VECTORS")
#           pragma RETAIN(Boot2App_Vector_Table)
const uint16_t Boot2App_Vector_Table[] =
#       endif
{
    (uint16_t) &TI_MSPBoot_CI_PHYDL_Init,   /*! Initialization routine */
    (uint16_t) &TI_MSPBoot_CI_PHYDL_Poll,    /*! Poll routine */
};
#endif  //CONFIG_CI_PHYDL_COMM_SHARED




