/*******************************************************************************
 * (c) Copyright 2009-2011 Actel Corporation.  All rights reserved.
 * 
 * CoreI2C software driver Application Programming Interface.
 * This file contains defines and function declarations allowing to interface
 * with the CoreI2C software driver.
 * 
 * SVN $Revision: 4129 $
 * SVN $Date: 2012-01-18 17:41:28 +0530 (Wed, 18 Jan 2012) $
 */
/*=========================================================================*//**
  @mainpage CoreI2C Bare Metal Driver.
   The CoreI2C bare metal software driver supports I2C master and slave
   operations.

  @section intro_sec Introduction
  CoreI2C driver provides a set of functions for controlling  Microsemi CoreI2C
  hardware IP. The driver supports up to 16 channels per CoreI2C instance.
  Since I2C-bus can be used in different control architectures like
  System Management Bus (SMBus), Power Management Bus (PMBus), Intelligent 
  Platform Management Interface (IPMI), this driver can be utilized as a part
  of the mentioned system design.
  
  The major features provided by CoreI2C driver are:
    - A function to configure I2C bus speed and slave address.
    - Functions to read and write data in both master and slave mode.
    - Functions to control and configure SMBus related features.
    - Functions to set the addressing mode for I2C slaves.

  This driver can be used as part of a bare metal system where no operating 
  system  is available. These drivers can be adapted for use as part of an 
  operating system, but the implementation of the adaptation layer between this 
  driver and  the operating system's driver model is outside the scope of this 
  driver.
  
  @section hw_dependencies Hardware Flow Dependencies
  Your application software should configure the CoreI2C driver through
  calls to the I2C_init() function for each CoreI2C instance in the
  hardware design.  The configuration parameters include the CoreI2C hardware
  instance base address and other runtime parameters, such as the I2C serial
  clock frequency and I2C device address.
  No CoreI2C hardware configuration parameters are used by the driver, apart
  from the CoreI2C hardware instance base address. Hence, no additional
  configuration files are required to use the driver. 
  
  Interrupt Control
  The CoreI2C driver has to enable and disable the generation of interrupts by
  CoreI2C at various times when it is operating. This enabling and disabling of
  interrupts must be done through the system’s interrupt controller. For that
  reason, the method of controlling the CoreI2C interrupt is system specific
  and it is necessary to customize the I2C_enable_irq() and I2C_disable_irq()
  functions. These functions are found in the file i2c_interrupt.c.
  Their default implementation fires an assertion to attract attention to the
  fact that these functions must be modified.
  The implementation of the I2C_enable_irq() function should permit interrupts
  generated by a CoreI2C instance to interrupt the processor. The implementation
  of the I2C_disable_irq() function should prevent interrupts generated by a
  CoreI2C instance from interrupting the processor. Please refer to the provided
  example projects for a working implementation of these functions.

  Note: You must include the path to any application header files that are
        included in the i2c_interrupt.c file, as an include path in your
        project’s compiler settings. The details of how to do this will depend
        on your development software.

  SMBus Logic options
  SMBus related APIs will not have any effect if in CoreI2C hardware
  configuration "Generate SMBus Logic" is not enabled. The APIs which will not
  give desired results in case SMBus Logic is disabled are I2C_smbus_init(),
  I2C_reset_smbus(), I2C_enable_smbus_irq(), I2C_disable_smbus_irq(),
  I2C_suspend_smbus_slave(),I2C_set_smsbus_alert(), I2C_get_irq_status().

  Fixed Baud Rate Values
  The serial clock frequency parameter passed to the I2C_init() and 
  I2C_channel_init() functions may not have any effect if fixed values were
  selected for Baud rate in the hardware configuration of CoreI2C. When fixed
  values are selected for these baud rates, the driver cannot overwrite
  the fixed values.

  Fixed Slave Address Options Values
  The primary slave address  parameter passed to the I2C_init() function, and
  secondary address value passed to the I2C_set_slave_second_addr() function,
  may not have the desired effect if fixed values were selected for the slave 0
  address and slave 1 address respectively.

  @section theory_op Theory of Operation
  The CoreI2C software driver is designed to allow the control of multiple
  instances of CoreI2C. Each instance of CoreI2C in the hardware design
  is associated with a single instance of the i2c_instance_t structure in t
  he software. You must allocate memory for one unique i2c_instance_t
  structure instance for each CoreI2C hardware instance. The contents of
  these data structures are initialized during calls to function I2C_init()
  or I2C_channel_init(). A pointer to the structure is passed to
  subsequent driver functions in order to identify the CoreI2C hardware
  instance you wish to perform the requested operation.

  Note: Do not attempt to directly manipulate the contents of i2c_instance_t
       structures. These structures are only intended to be modified by the driver
       functions.

  The CoreI2C driver functions are grouped into the following categories:
    - Initialization and configuration functions
    - Interrupt control
    - I2C slave addressing functions
    - I2C master operations functions to handle write, read and write-read 
      transactions
    - I2C slave operations functions to handle write, read and write-read 
      transactions
    - Mixed master-slave operations handle pending transactions. I2C device is
      in slave receiver or transmitter mode and if it tries to acquire the bus by
      starting master transaction, the driver will hold the master transaction
      information and starts the pending operation once it gets control of the bus.
    - SMBus related functions to handle SMBALERT interrupt, power state status and reset
      of SMbus devices.
    
  Initialization and Configuration
    The CoreI2C device is first initialized through a call to the I2C_init() function.
    Since CoreI2C peripheral supports up to 16 channels, an additional function
    I2C_channel_init() is required to initialize other channels with its own data
    structures. I2C_init() function initializes channel 0 of CoreI2C and acts as base
    for next channel initialization as the hardware base address and I2C serial address
    are same across all the channels.
    I2C_init() function must be called before any other  I2C driver function calls.
    The I2C_init() function call for each I2C takes the I2C serial address
    assigned to the  I2C and the serial clock divider to be used to generate its I2C 
    clock as configuration parameters.
    I2C_channel_init() function takes input parameter as pointer to CoreI2C instance which
    is updated in I2C_init() function. Other input parameter which is required by 
    this function is serial clock divider which generates its I2C clock. This function 
    updates I2C channel instance data structure which will be further used in different 
    functions. 
    
  Interrupt Control
    CoreI2C driver uses three types of interrupts to carry out I2C functionalities.
    The most important interrupt is INTR which drives the state machine of CoreI2C
    driver. The CoreI2C driver enables and disables the generation of interrupts by
    I2C at various times when it is operating. The application user must configure
    the CoreI2C INTR interrupt handler and call I2C_isr() function in the 
    handler to carry out I2C transactions smoothly.
    The function I2C_register_write_handler() is used to register a write
    handler function with the CoreI2C driver that it will call on completion of
    an I2C write transaction by the CoreI2C slave. It is application user
    responsibility to create and register the implementation of this handler 
    function that will process or trigger the processing of the received data.

    Other interrupts are related to SMBus and are enabled and disabled through
    I2C_enable_smbus_irq(), I2C_disable_smbus_irq() respectively. The users must
    design interrupt handler functions in their application to get desired
    response for SMBus related interrupts.

  I2C slave addressing functions
    CoreI2C can be addressed using three addressing mode:
    - Slave address 0 - This is first slave address and it is primary address
                        which is used for I2C transactions. CoreI2C must configure slave
                        address during I2C_init() function call.
    - Slave address 1 - This is second slave address might be required in certain
                        application specific scenario. Second slave address can be
                        configured using I2C_set_slave_second_addr(). The second slave
                        address can be disabled using I2C_disable_slave_second_addr()
                        function.
    - General call addressing - CoreI2C slave can be addressed through broadcast command
                        by master using slave address of 0x00. The slave must have general
                        call addressing enabled using I2C_set_gca() function to respond to
                        master commands. If particular I2C slave does not require to respond
                        to general addressing call, it should disable it by
                        call to I2C_clear_gca().

  Transaction Types
    The  I2C driver is designed to handle three types of I2C transaction:
      Write transactions
      Read transactions
      Write-read transactions
 
    Write transaction
      The master I2C device initiates a write transaction by sending a START bit
      as soon as the bus becomes free. The START bit is followed by the 7-bit
      serial address of the target slave device followed by the read/write bit
      indicating the direction of the transaction. The slave acknowledges the receipt
      of it's address with an acknowledge bit. The master sends data one byte at
      a time to the slave, which must acknowledge receipt of each byte for the
      next byte to be sent. The master sends a STOP bit to complete the transaction.
      The slave can abort the transaction by replying with a non-acknowledge bit
      instead of an acknowledge.
      The application programmer can choose not to send a STOP bit at the end of
      the transaction causing the next transaction to begin with a repeated START bit.
      
    Read transaction
      The master I2C device initiates a read transaction by sending a START bit
      as soon as the bus becomes free. The START bit is followed by the 7-bit
      serial address of the target slave device followed by the read/write bit
      indicating the direction of the transaction. The slave acknowledges receipt
      of it's slave address with an acknowledge bit. The slave sends data one byte
      at a time to the master, which must acknowledge receipt of each byte for the
      next byte to be sent. The master sends a non-acknowledge bit following the
      last byte it wishes to read followed by a STOP bit.
      The application programmer can choose not to send a STOP bit at the end of
      the transaction causing the next transaction to begin with a repeated START bit.
 
    Write-read transaction
      The write-read transaction is a combination of a write transaction
      immediately followed by a read transaction. There is no STOP bit between
      the write and read phases of a write-read transaction. A repeated START
      bit is sent between the write and read phases.
      The write-read transaction is typically used to send a command or offset
      in the write transaction specifying the logical data to be transferred
      during the read phase.
      The application programmer can choose not to send a STOP bit at the end of
      the transaction causing the next transaction to begin with a repeated START bit.

  Master Operations
    The application can use the I2C_write(), I2C_read() and I2C_write_read()
    functions to initiate an I2C bus transaction. The application can then wait
    for the transaction to complete using the I2C_wait_complete() function
    or poll the status of the I2C transaction using the I2C_get_status()
    function until it returns a value different from I2C_IN_PROGRESS.

  Slave Operations
    The configuration of the  I2C driver to operate as an I2C slave requires
    the use of the following functions:
       I2C_set_slave_tx_buffer()
       I2C_set_slave_rx_buffer()
       I2C_set_slave_mem_offset_length()
       I2C_register_write_handler()
       I2C_enable_slave()
    Use of all functions is not required if the slave I2C does not need to support
    all types of I2C read transactions. The subsequent sections list the functions
    that must be used to support each transaction type. 
    
     Responding to read transactions
      The following functions are used to configure the  I2C driver to respond
      to I2C read transactions:
        I2C_set_slave_tx_buffer()
        I2C_enable_slave()
      The function I2C_set_slave_tx_buffer() specifies the data buffer that
      will be transmitted when the I2C slave is the target of an I2C read
      transaction. It is then up to the application to manage the content of that
      buffer to control the data that will be transmitted to the I2C master as a
      result of the read transaction.
      The function I2C_enable_slave() enables the  I2C hardware instance
      to respond to I2C transactions. It must be called after the  I2C driver
      has been configured to respond to the required transaction types.

     Responding to write transactions
      The following functions are used to configure the  I2C driver to respond
      to I2C write transactions:
        I2C_set_slave_rx_buffer()
        I2C_register_write_handler()
        I2C_enable_slave()
      The function I2C_set_slave_rx_buffer() specifies the data buffer that
      will be used to store the data received by the I2C slave when it is the
      target an I2C  write transaction.
      The function I2C_register_write_handler() specifies the handler function
      that must be called on completion of the I2C write transaction. It is this
      handler function that processes or triggers the processing of the received
      data.
      The function I2C_enable_slave() enables the  I2C hardware instance
      to respond to I2C transactions. It must be called after the  I2C driver
      has been configured to respond to the required transaction types.

     Responding to write-read transactions
      The following functions are used to configure the  I2C driver to respond
      to write-read transactions:
        I2C_set_slave_tx_buffer()
        I2C_set_slave_rx_buffer()
        I2C_set_slave_mem_offset_length()
        I2C_enable_slave()
      The function I2C_set_slave_mem_offset_length() specifies the number of
      bytes expected by the I2C slave during the write phase of the write-read
      transaction.
      The function I2C_set_slave_tx_buffer() specifies the data that will be
      transmitted to the I2C master during the read phase of the write-read
      transaction. The value received by the I2C slave during the write phase of
      the transaction will be used as an index into the transmit buffer specified
      by this function to decide which part of the transmit buffer will be
      transmitted to the I2C master as part of the read phase of the write-read
      transaction.
      The function I2C_set_slave_rx_buffer() specifies the data buffer that
      will be used to store the data received by the I2C slave during the write
      phase of the write-read transaction. This buffer must be at least large
      enough to accommodate the number of bytes specified through the
      I2C_set_slave_mem_offset_length() function.
      The function I2C_enable_slave() enables the  I2C hardware
      instance to respond to I2C transactions. It must be called after the 
      I2C driver has been configured to respond to the required transaction types.

  Mixed Master-Slave Operations
      The I2C device is capable of mixed operations, that is, it can try to
      become master in middle of its slave operation. Once the bus is released,
      this I2C device can acquire bus and starts its previously pended
      transaction. The I2C device stores both master transmitter/receiver
      and slave transmitter/receiver information at any given time to avoid
      corruption of data. The I2C device will switch from slave to master based
      on the current transaction on the bus.  So, both master and slave functions
      can be used for the I2C device to operate in mixed mode. The slave mode and
      master mode operations details are described in the previous section.

  SMBus Control
    The CoreI2C driver initializes SMBus related features using I2C_smbus_init().
    The SMBUS slave can be forced to power down or suspend mode by the I2C master
    using I2C_suspend_smbus_slave().
    The  SMBus device can be reset using I2C_reset_smbus().
    I2C_set_smsbus_alert() function is used to force master communication
    by the I2C slave device. SMBus related interrupt functions are
    I2C_enable_smbus_irq(), I2C_disable_smbus_irq().

 *//*=========================================================================*/

#ifndef CORE_I2C_H_
#define CORE_I2C_H_

#include "cpu_types.h"
#ifdef __cplusplus
extern "C" {
#endif 

/*-------------------------------------------------------------------------*//**
  The I2C_RELEASE_BUS constant is used to specify the options parameter to
  functions I2C_read(), I2C_write() and I2C_write_read() to indicate
  that a STOP bit must be generated at the end of the I2C transaction to release
  the bus.
 */
#define I2C_RELEASE_BUS     0x00u

/*-------------------------------------------------------------------------*//**
  The I2C_HOLD_BUS constant is used to specify the options parameter to
  functions I2C_read(), I2C_write() and I2C_write_read() to indicate
  that a STOP bit must not be generated at the end of the I2C transaction in
  order to retain the bus ownership. This causes the next transaction to
  begin with a repeated START bit and no STOP bit between the transactions.
 */
#define I2C_HOLD_BUS        0x01u

/*-------------------------------------------------------------------------*//**
 The maximum number of I2C channel supported by CoreI2C peripheral is 16.
*/
#define I2C_MAX_CHANNEL_NUMBER 16u

/*-------------------------------------------------------------------------*//**
  I2C Channel 0 constant definition. This is the basic channel which will be
  at least supported by any CoreI2C instance.
 */
#define I2C_CHANNEL_0          0u

/*-------------------------------------------------------------------------*//**
  I2C Channel 16 constant definition. This is the last channel which can be
  supported by any CoreI2C instance.
 */
#define I2C_CHANNEL_16        (I2C_MAX_CHANNEL_NUMBER - 1u)

/*-------------------------------------------------------------------------*//**
  Following constants specify the interrupt identifier number which will be
  solely used by driver API. This has nothing to do with hardware interrupt line.
  I2C_INT_IRQ is basic interrupt signal which drives the state machine
  of CoreI2C driver. The I2C_SMBALERT_IRQ and I2C_SMBUS_IRQ are used by SMBus
  interrupt enable and disable functions. These IRQ numbers are also used by
  I2C_get_irq_status() function.
 */
#define I2C_NO_IRQ             0x00u
#define I2C_SMBALERT_IRQ       0x01u
#define I2C_SMBSUS_IRQ         0x02u
#define I2C_INTR_IRQ           0x04u

/*-------------------------------------------------------------------------*//**
  The i2c_clock_divider_t type is used to specify the divider to be applied
  to the I2C PCLK or BCLK signal in order to generate the I2C clock.
 */
typedef enum i2c_clock_divider {
    I2C_PCLK_DIV_256 = 0u,
    I2C_PCLK_DIV_224,
    I2C_PCLK_DIV_192,
    I2C_PCLK_DIV_160,
    I2C_PCLK_DIV_960,
    I2C_PCLK_DIV_120,
    I2C_PCLK_DIV_60,
    I2C_BCLK_DIV_8
} i2c_clock_divider_t;

/*-------------------------------------------------------------------------*//**
  The i2c_status_t type is used to report the status of I2C transactions.
 */
typedef enum i2c_status
{
    I2C_SUCCESS = 0u,
    I2C_IN_PROGRESS,
    I2C_FAILED
} i2c_status_t;
/*-------------------------------------------------------------------------*//**
  The i2c_slave_handler_ret_t type is used by slave write handler functions
  to indicate whether or not the received data buffer should be released.
 */
typedef enum i2c_slave_handler_ret {
    I2C_REENABLE_SLAVE_RX = 0u,
    I2C_PAUSE_SLAVE_RX = 1u
} i2c_slave_handler_ret_t;

typedef struct i2c_instance i2c_instance_t ;

/*-------------------------------------------------------------------------*//**
  Slave write handler functions prototype.
  ------------------------------------------------------------------------------
  This defines the function prototype that must be followed by  I2C slave
  write handler functions. These functions are registered with the  CoreI2C driver
  through the I2C_register_write_handler() function.

  Declaring and Implementing Slave Write Handler Functions:
    Slave write handler functions should follow the following prototype:
    i2c_slave_handler_ret_t write_handler
    ( 
        i2c_instance_t *instance, uint8_t * data, uint16_t size 
    );

    The instance parameter is a pointer to i2c_instance_t for which this
    slave write handler has been declared.
    The data parameter is a pointer to a buffer (received data buffer) holding
    the data written to the I2C slave.
    The size parameter is the number of bytes held in the received data buffer.
    Handler functions must return one of the following values:
        I2C_REENABLE_SLAVE_RX
        I2C_PAUSE_SLAVE_RX.
    If the handler function returns I2C_REENABLE_SLAVE_RX, the driver
    releases the received data buffer and allows further I2C write transactions to
    the  I2C slave to take place.
    If the handler function returns I2C_PAUSE_SLAVE_RX, the I2C slave
    responds to subsequent write requests with a non-acknowledge bit (NACK),
    until the received data buffer content has been processed by some other part
    of the software application.
    A call to I2C_enable_slave() is required at some point after
    returning I2C_PAUSE_SLAVE_RX in order to release the received data
    buffer so it can be used to store data received by subsequent I2C write
    transactions.
 */
typedef i2c_slave_handler_ret_t (*i2c_slave_wr_handler_t)(i2c_instance_t *instance, uint8_t *, uint16_t );


/*-------------------------------------------------------------------------*//**
  i2c_instance_t
  ------------------------------------------------------------------------------
  The content of this data structure should not be modified or used outside of
  the CoreI2C driver. Software using the CoreI2C driver should only need to 
  create one single instance of this data structure for each CoreI2C hardware
  instance in the system then pass a pointer to these data structures with each
  call to the CoreI2C driver in order to identify the CoreI2C hardware instance
  it wishes to use.
 */
struct i2c_instance
{
    addr_t base_address;
    uint_fast8_t ser_address;

    /* Channel number*/
    uint8_t channel_number;

    /* Transmit related info:*/
    uint_fast8_t target_addr;

    /* Current transaction type (WRITE, READ, RANDOM_READ)*/
    uint8_t transaction;
    
    uint_fast16_t random_read_addr;

    uint8_t options;
    
    /* Master TX INFO: */
    const uint8_t * master_tx_buffer;
    uint_fast16_t master_tx_size;
    uint_fast16_t master_tx_idx;
    uint_fast8_t dir;
    
    /* Master RX INFO: */
    uint8_t * master_rx_buffer;
    uint_fast16_t master_rx_size;
    uint_fast16_t master_rx_idx;

    /* Master Status */
    volatile i2c_status_t master_status;

    /* Slave TX INFO */
    const uint8_t * slave_tx_buffer;
    uint_fast16_t slave_tx_size;
    uint_fast16_t slave_tx_idx;
    
    /* Slave RX INFO */
    uint8_t * slave_rx_buffer;
    uint_fast16_t slave_rx_size;
    uint_fast16_t slave_rx_idx;
    /* Slave Status */
    volatile i2c_status_t slave_status;
    
    /* Slave data: */
    uint_fast8_t slave_mem_offset_length;
    i2c_slave_wr_handler_t slave_write_handler;
    uint8_t is_slave_enabled;
    
    /* user  specific data */
    void *p_user_data ;

    /* I2C bus status */
    uint8_t bus_status;

    /* Is transaction pending flag */
    uint8_t is_transaction_pending;

    /* I2C Pending transaction */
    uint8_t pending_transaction;
};

/*-------------------------------------------------------------------------*//**
  I2C initialization routine.
  ------------------------------------------------------------------------------
  The I2C_init() function initializes and configures hardware with the CoreI2C
  base address and serial address. It also configures serial clock divider
  and initializes data structures of channel 0 of the CoreI2C instance. 
  Since all the channels of an instance of CoreI2C share same hardware base 
  address and serial slave address, this function will be first called before 
  all the other channels initialization.
  If only one channel is configured in the system design, the pointer to 
  coreI2C data structure i2c_instance_t used in I2C_Init() will also be 
  used in subsequent calls to the CoreI2C driver functions. If more than one 
  channel is configured in the system, I2C_channel_init() will be called after 
  I2C_init(), which initializes I2C data structure with its channel parameters.
  The pointer to data structure i2c_instance_t used for the particular channel 
  will be taken as input parameter to subsequent function calls.

  @param this_i2c:
    Pointer to the i2c_instance_t data structure holding all data related to the
    CoreI2C instance being initialized. This data structure contains information
    related to an I2C channel. A pointer to the same data structure will
    be used in subsequent calls to the CoreI2C driver functions in order to
    identify the CoreI2C instance that should perform the operation implemented
    by the called  driver function.
  
  @param base_address:
    Base address in the processor's memory map of the CoreI2C instance being
    Initialized.
 
  @param ser_address:
    This parameter sets the I2C serial address being initialized. It is the I2C
    bus address to which the CoreI2C instance will respond. CoreI2C can operate
    in master or slave mode. The serial address is significant only in case of
    I2C slave mode. In master mode, CoreI2C does not require serial address and 
    providing any value to this parameter does not have any effect. If you do
    not intend to use the CoreI2C device in slave mode, then any dummy slave 
    address value can be provided to this parameter. However, in case
    of CoreI2C switching from master to slave , it is advisable to initialize
    the CoreI2C device with configured serial slave address. 
    You need to call I2C_init() function whenever it requires to change the slave
    address as there is no separate function to set slave address of I2C device.
    The serial address being initialized through this function is basically primary
    slave address or slave address0. I2C_set_second_slave_addr() can be used to
    set secondary slave address or slave address 1.    
    Note : ser_address parameter does not have any affect if fixed slave 
           address is enabled in CoreI2C hardware design. CoreI2C will 
           be always addressed with the hardware configured fixed slave
           address.
           
    Note : ser_address parameter will not have any affect if CoreI2C instance is
           only used in master mode.

  @param ser_clock_speed:
    This parameter sets the I2C serial clock frequency. It selects the divider
    that will be used to generate the serial clock from the APB PCLK or from
    the BCLK. It can be one of the following:
        I2C_PCLK_DIV_256
        I2C_PCLK_DIV_224
        I2C_PCLK_DIV_192
        I2C_PCLK_DIV_160
        I2C_PCLK_DIV_960
        I2C_PCLK_DIV_120
        I2C_PCLK_DIV_60
        I2C_BCLK_DIV_8
    
    Note: serial_clock_speed value will have no affect if Fixed baud rate is 
          enabled in CoreI2C hardware instance configuration dialogue window. 
          The fixed baud rate divider value will override the value
          passed as parameter in this function.
  
  @return none.  
  
  Example:
  @code
   #define COREI2C_BASE_ADDR  0xC0000000u
   #define COREI2C_SER_ADDR   0x10u

   i2c_instance_t g_i2c_inst;

   void system_init( void )
   {
        I2C_init( &g_i2c_inst, COREI2C_BASE_ADDR, COREI2C_SER_ADDR, I2C_PCLK_DIV_256 ) ;
   }
  @endcode
 */
void I2C_init
(
    i2c_instance_t * this_i2c,
    addr_t base_address,
    uint8_t ser_address,
    i2c_clock_divider_t ser_clock_speed
);

/*-------------------------------------------------------------------------*//**
  I2C channel initialization routine.
  ------------------------------------------------------------------------------
  The I2C_channel_init() function initializes and configures hardware and data
  structures of one of the channel of CoreI2C instance.
  I2C_init() function must be called before calling this function to set
  CoreI2C instance hardware base address and I2C serial address.
  I2C_channel_init() function also initializes I2C serial clock divider to set
  serial clock baud rate.  
  The pointer to data structure i2c_instance_t used for the particular channel
  will be taken as input parameter to subsequent function calls.
  
  @param this_i2c_channel
    Pointer to the i2c_instance_t data structure holding all data related to the
    CoreI2C channel being initialized. A pointer to the same data structure will
    be used in subsequent calls to the CoreI2C driver functions in order to
    identify the CoreI2C channel instance that should perform the operation 
    implemented by the called  driver function.

  @param this_i2c:
    This parameter passes in the i2c_instance_t structure previously initialized
    by I2C_init(). It holds information regarding hardware base address and
    I2C serial address. The information are required by this_i2c_channel as
    all channel shares same base address and serial address.

  @param channel_number:
    This parameter identifies the channel number.

  @param ser_clock_speed:
    This parameter sets the I2C serial clock frequency. It selects the divider
    that will be used to generate the serial clock from the APB PCLK or from
    the BCLK. It can be one of the following:
        I2C_PCLK_DIV_256
        I2C_PCLK_DIV_224
        I2C_PCLK_DIV_192
        I2C_PCLK_DIV_160
        I2C_PCLK_DIV_960
        I2C_PCLK_DIV_120
        I2C_PCLK_DIV_60
        I2C_BCLK_DIV_8
    
    Note: serial_clock_speed value will have no affect if Fixed baud rate is 
          enabled in CoreI2C hardware instance configuration dialogue window. 
          The fixed baud rate divider value will supersede the value 
          passed as parameter in this function.
  
  @return none.  
  
  Example:
  @code
   #define COREI2C_BASE_ADDR  0xC0000000u
   #define COREI2C_SER_ADDR   0x10u
   #define CHANNEL_1             1u
   #define DATA_LENGTH           16u

   i2c_instance_t g_i2c_inst;
   i2c_instance_t g_i2c_channel_1_inst;

   uint8_t  tx_buffer[DATA_LENGTH];
   uint8_t  write_length = DATA_LENGTH ;     

   void system_init( void )
   {
        uint8_t  target_slave_addr = 0x12 ;
        
        // Initialize base CoreI2C instance
        I2C_init( &g_i2c_inst, COREI2C_BASE_ADDR, COREI2C_SER_ADDR, I2C_PCLK_DIV_256 ) ;
        
        // Initialize CoreI2C channel 1 with different clock speed
        I2C_channel_init( &g_i2c_channel_1_inst, &g_i2c_inst, CHANNEL_1, I2C_PCLK_DIV_224 ) ;
        
        // Write data to Channel 1 of CoreI2C instance.
        I2C_write( &g_i2c_channel_1_inst, target_slave_addr, tx_buffer, write_length, I2C_RELEASE_BUS );
   }
  @endcode
          
*/
void I2C_channel_init
(
    i2c_instance_t * this_i2c_channel,
    i2c_instance_t * this_i2c,
    uint8_t channel_number,
    i2c_clock_divider_t ser_clock_speed
);

/*------------------------------------------------------------------------------
  CoreI2C interrupt service routine.
  ------------------------------------------------------------------------------
    The function I2C_isr is CoreI2C interrupt service routine. User must call
    this function from their application level CoreI2C interrupt handler
    function. This function runs the I2C state machine based on previous and 
    current status.

    @param this_i2c:
    The this_i2c parameter is pointer to the i2c_instance_t data structure holding
    all data related to a specific CoreI2C channel. For example, if only one 
    channel is initialized, this data structure holds the information of channel 
    0 of the instantiated CoreI2C hardware.
    
    @return none
    
    Example:
    @code
    
    #define COREI2C_BASE_ADDR  0xC0000000u
    #define COREINTERRUPT_BASE_ADDR  0xCC000000u
    #define COREI2C_SER_ADDR   0x10u
    #define I2C_IRQ_NB           2u
    
    i2c_instance_t g_i2c_inst;

    void core_i2c_isr( void )
    {
       I2C_isr( &g_i2c_inst );
    }
    
   void main( void )
    {
        CIC_init( COREINTERRUPT_BASE_ADDR );
        NVIC_init();
        CIC_set_irq_handler( I2C_IRQ_NB, core_i2c_isr );
        I2C_init( &g_i2c_inst, COREI2C_BASE_ADDR, COREI2C_SER_ADDR, I2C_PCLK_DIV_256 ) ;
        NVIC_enable_interrupt( NVIC_IRQ_0 );
    }
    @endcode
 */
void I2C_isr
(
    i2c_instance_t * this_i2c
);

/*******************************************************************************
 *******************************************************************************
 * 
 *                           Master specific functions
 * 
 * The following functions are only used within an I2C master's implementation.
 */
 
/*-------------------------------------------------------------------------*//**
  I2C master write function.
  ------------------------------------------------------------------------------
  This function initiates an I2C master write transaction. This function returns
  immediately after initiating the transaction. The content of the write buffer
  passed as parameter should not be modified until the write transaction
  completes. It also means that the memory allocated for the write buffer should
  not be freed or should not go out of scope before the write completes. You can
  check for the write transaction completion using the I2C_status() function.
  ------------------------------------------------------------------------------
  @param this_i2c:
    The this_i2c parameter is pointer to the i2c_instance_t data structure holding
    all data related to a specific CoreI2C channel. For example, if only one 
    channel is initialized, this data structure holds the information of channel 
    0 of the instantiated CoreI2C hardware.

  @param serial_addr:
    This parameter specifies the serial address of the target I2C device.
  
  @param write_buffer:
    This parameter is a pointer to a buffer holding the data to be written to
    the target I2C device.
    Care must be taken not to release the memory used by this buffer before the
    write transaction completes. For example, it is not appropriate to return
    from a function allocating this buffer as an array variable before the write
    transaction completes as this would result in the buffer's memory being
    de-allocated from the stack when the function returns. This memory could
    then be subsequently reused and modified causing unexpected data to be
    written to the target I2C device.
  
  @param write_size:
    Number of bytes held in the write_buffer to be written to the target I2C
    device.
 
  @param options:
    The options parameter is used to indicate if the I2C bus should be released
    on completion of the write transaction. Using the I2C_RELEASE_BUS
    constant for the options parameter causes a STOP bit to be generated at the
    end of the write transaction causing the bus to be released for other I2C
    devices to use. Using the I2C_HOLD_BUS constant as options parameter
    prevents a STOP bit from being generated at the end of the write
    transaction, preventing other I2C devices from initiating a bus transaction.
  
  @return none.  
  
  Example:
  @code
   #define COREI2C_BASE_ADDR  0xC0000000u
   #define COREI2C_DUMMY_ADDR   0x10u
   #define DATA_LENGTH           16u

   i2c_instance_t g_i2c_inst;

   uint8_t  tx_buffer[DATA_LENGTH];
   uint8_t  write_length = DATA_LENGTH ;     

   void main( void )
   {
        uint8_t  target_slave_addr = 0x12 ;
        
        // Initialize base CoreI2C instance
        I2C_init( &g_i2c_inst, COREI2C_BASE_ADDR, COREI2C_DUMMY_ADDR, I2C_PCLK_DIV_256 ) ;
        
        // Write data to Channel 0 of CoreI2C instance.
        I2C_write( &g_i2c_inst, target_slave_addr, tx_buffer, write_length, RELEASE_BUS );
   }
  @endcode
 */
void I2C_write
(
    i2c_instance_t * this_i2c,
    uint8_t serial_addr,
    const uint8_t * write_buffer,
    uint16_t write_size,
    uint8_t options
);

/*-------------------------------------------------------------------------*//**
  I2C master read.
  ------------------------------------------------------------------------------
  This function initiates an I2C master read transaction. This function returns
  immediately after initiating the transaction.
  The content of the read buffer passed as parameter should not be modified
  until the read transaction completes. It also means that the memory allocated
  for the read buffer should not be freed or should not go out of scope before
  the read completes. You can check for the read transaction completion using the
  I2C_status() function.
  ------------------------------------------------------------------------------
  @param this_i2c:
    The this_i2c parameter is pointer to the i2c_instance_t data structure holding
    all data related to a specific CoreI2C channel. For example, if only one 
    channel is initialized, this data structure holds the information of channel 
    0 of the instantiated CoreI2C hardware.
  
  @param serial_addr:
    This parameter specifies the serial address of the target I2C device.
  
  @param read_buffer
    This is a pointer to a buffer where the data received from the target device
    will be stored.
    Care must be taken not to release the memory used by this buffer before the
    read transaction completes. For example, it is not appropriate to return
    from a function allocating this buffer as an array variable before the read
    transaction completes as this would result in the buffer's memory being
    de-allocated from the stack when the function returns. This memory could
    then be subsequently reallocated resulting in the read transaction
    corrupting the newly allocated memory. 

  @param read_size:
    This parameter is the number of bytes to read from the target device. This
    size must not exceed the size of the read_buffer buffer.
 
  @param options:
    The options parameter is used to indicate if the I2C bus should be released
    on completion of the read transaction. Using the I2C_RELEASE_BUS
    constant for the options parameter causes a STOP bit to be generated at the
    end of the read transaction causing the bus to be released for other I2C
    devices to use. Using the I2C_HOLD_BUS constant as options parameter
    prevents a STOP bit from being generated at the end of the read transaction,
    preventing other I2C devices from initiating a bus transaction.
    
  @return none.  
  
  Example:
  @code
   #define COREI2C_BASE_ADDR  0xC0000000u
   #define COREI2C_DUMMY_ADDR   0x10u
   #define DATA_LENGTH           16u

   i2c_instance_t g_i2c_inst;

   uint8_t  rx_buffer[DATA_LENGTH];
   uint8_t  read_length = DATA_LENGTH ;     

   void main( void )
   {
        uint8_t  target_slave_addr = 0x12 ;
        i2c_status_t status;
        
        // Initialize base CoreI2C instance
        I2C_init( &g_i2c_inst, COREI2C_BASE_ADDR, COREI2C_DUMMY_ADDR, I2C_PCLK_DIV_256 ) ;
        
        // Write data to Channel 0 of CoreI2C instance.
        I2C_read( &g_i2c_inst, target_slave_addr, rx_buffer, read_length, I2C_RELEASE_BUS );
        
        status = I2C_wait_complete( &g_i2c_inst );
    }
  @endcode
 */
void I2C_read
(
    i2c_instance_t * this_i2c,
    uint8_t serial_addr,
    uint8_t * read_buffer,
    uint16_t read_size,
    uint8_t options
);

/*-------------------------------------------------------------------------*//**
  I2C master write-read
  ------------------------------------------------------------------------------
  This function initiates an I2C write-read transaction where data is first
  written to the target device before issuing a restart condition and changing
  the direction of the I2C transaction in order to read from the target device.
  ------------------------------------------------------------------------------
  @param this_i2c:
    The this_i2c parameter is pointer to the i2c_instance_t data structure holding
    all data related to a specific CoreI2C channel. For example, if only one 
    channel is initialized, this data structure holds the information of channel 
    0 of the instantiated CoreI2C hardware.
  
  @param serial_addr:
    This parameter specifies the serial address of the target I2C device.
  
  @param addr_offset:
    This parameter is a pointer to the buffer containing the data that will be
    sent to the slave during the write phase of the write-read transaction. This
    data is typically used to specify an address offset specifying to the I2C
    slave device what data it must return during the read phase of the
    write-read transaction.
  
  @param offset_size:
    This parameter specifies the number of offset bytes to be written during the
    write phase of the write-read transaction. This is typically the size of the
    buffer pointed to by the addr_offset parameter.
  
  @param read_buffer:
    This parameter is a pointer to the buffer where the data read from the I2C
    slave will be stored.
  
  @param read_size:
    This parameter specifies the number of bytes to read from the target I2C
    slave device. This size must not exceed the size of the buffer pointed to by
    the read_buffer parameter.
 
  @param options:
    The options parameter is used to indicate if the I2C bus should be released
    on completion of the write-read transaction. Using the I2C_RELEASE_BUS
    constant for the options parameter causes a STOP bit to be generated at the
    end of the write-read transaction causing the bus to be released for other
    I2C devices to use. Using the I2C_HOLD_BUS constant as options parameter
    prevents a STOP bit from being generated at the end of the write-read
    transaction, preventing other I2C devices from initiating a bus transaction.
        
  @return none.  
  
  Example:
  @code
   #define COREI2C_BASE_ADDR  0xC0000000u
   #define COREI2C_DUMMY_ADDR   0x10u
   #define TX_LENGTH           16u
   #define RX_LENGTH            8u

   i2c_instance_t g_i2c_inst;
   uint8_t  rx_buffer[RX_LENGTH];
   uint8_t  read_length = RX_LENGTH ;     
   uint8_t  tx_buffer[TX_LENGTH];
   uint8_t  write_length = TX_LENGTH ;  
   void main( void )
   {
        uint8_t  target_slave_addr = 0x12 ;
        i2c_status_t status;
        // Initialize base CoreI2C instance
        I2C_init( &g_i2c_inst, COREI2C_BASE_ADDR, COREI2C_DUMMY_ADDR, I2C_PCLK_DIV_256 ) ;
        I2C_write_read
        (
            &g_i2c_inst,
            target_slave_addr,
            tx_buffer,
            write_length,
            rx_buffer,
            read_length,
            I2C_RELEASE_BUS
        );
        status = I2C_wait_complete( &g_i2c_inst );
    }
  @endcode
 */
void I2C_write_read
(
    i2c_instance_t * this_i2c,
    uint8_t serial_addr,
    const uint8_t * addr_offset,
    uint16_t offset_size,
    uint8_t * read_buffer,
    uint16_t read_size,
    uint8_t options
);
    
/*-------------------------------------------------------------------------*//**
  I2C status
  ------------------------------------------------------------------------------
  This function indicates the current state of a  I2C instance.
  ------------------------------------------------------------------------------
  @param this_i2c:
    The this_i2c parameter is pointer to the i2c_instance_t data structure holding
    all data related to a specific CoreI2C channel. For example, if only one 
    channel is initialized, this data structure holds the information of channel 
    0 of the instantiated CoreI2C hardware.
  ------------------------------------------------------------------------------
  @return
    The return value indicates the current state of a  I2C instance or the
    outcome of the previous transaction if no transaction is in progress. 
    Possible return values are:
      I2C_SUCCESS
        The last I2C transaction has completed successfully.  
      I2C_IN_PROGRESS
        There is an I2C transaction in progress.
      I2C_FAILED
        The last I2C transaction failed.
  
  Example:
  @code
  
     i2c_instance_t g_i2c_inst;
    // Following function call gets the current status of CoreI2C driver
    i2c_status_t status = I2C_get_status( &g_i2c_inst );
    
  @endcode
 */
i2c_status_t I2C_get_status
(
    i2c_instance_t * this_i2c
);

/*-------------------------------------------------------------------------*//**
  Wait for I2C transaction completion.
  ------------------------------------------------------------------------------
  This function waits for the current I2C transaction to complete. The return
  value indicates whether the last I2C transaction was successful, or is still
  in progress, or failed.
  ------------------------------------------------------------------------------
  @param this_i2c:
    The this_i2c parameter is pointer to the i2c_instance_t data structure holding
    all data related to a specific CoreI2C channel. For example, if only one 
    channel is initialized, this data structure holds the information of channel 
    0 of the instantiated CoreI2C hardware.
  ------------------------------------------------------------------------------
  @return
    The return value indicates the outcome of the last I2C transaction. It can
    be one of the following: 
      I2C_SUCCESS
        The last I2C transaction has completed successfully.
      I2C_IN_PROGRESS
        The current I2C transaction is still in progress.
      I2C_FAILED
        The last I2C transaction failed.

  Example:
  @code
   #define COREI2C_BASE_ADDR  0xC0000000u
   #define COREI2C_DUMMY_ADDR   0x10u
   #define DATA_LENGTH           16u

   i2c_instance_t g_i2c_inst;
   uint8_t  rx_buffer[DATA_LENGTH];
   uint8_t  read_length = DATA_LENGTH ;     

   void main( void )
   {
        uint8_t  target_slave_addr = 0x12 ;
        i2c_status_t status;
        
        // Initialize base CoreI2C instance
        I2C_init( &g_i2c_inst, COREI2C_BASE_ADDR, COREI2C_DUMMY_ADDR, I2C_PCLK_DIV_256 ) ;
        // Write data to Channel 0 of CoreI2C instance.
        I2C_read( &g_i2c_inst, target_slave_addr, rx_buffer, read_length, I2C_RELEASE_BUS );
        status = I2C_wait_complete( &g_i2c_inst );
    }
  @endcode
 */
i2c_status_t I2C_wait_complete
(
    i2c_instance_t * this_i2c
);


/*******************************************************************************
 *******************************************************************************
 * 
 *                           Slave specific functions
 * 
 * The following functions are only used within the implementation of an I2C
 * slave device.
 */

/*-------------------------------------------------------------------------*//**
  I2C slave transmit buffer configuration.
  ------------------------------------------------------------------------------
  This function specifies the memory buffer holding the data that will be sent
  to the I2C master when this  I2C instance is the target of an I2C read or
  write-read transaction.
  ------------------------------------------------------------------------------
  @param this_i2c:
    The this_i2c parameter is pointer to the i2c_instance_t data structure holding
    all data related to a specific CoreI2C channel. For example, if only one 
    channel is initialized, this data structure holds the information of channel 
    0 of the instantiated CoreI2C hardware.
  
  @param tx_buffer:
    This parameter is a pointer to the memory buffer holding the data to be
    returned to the I2C master when this  I2C instance is the target of an
    I2C read or write-read transaction.
  
  @param tx_size:
    Size of the transmit buffer pointed to by the tx_buffer parameter.

  @return none.  
      
  Example:
  @code
   #define COREI2C_BASE_ADDR  0xC0000000u
   #define I2C_SLAVE_SER_ADDR   0x10u
   #define SLAVE_TX_BUFFER_SIZE   10u

   i2c_instance_t g_i2c_inst;

   uint8_t g_slave_tx_buffer[SLAVE_TX_BUFFER_SIZE] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 , 10 } ;

   void main( void )
   {
         // Initialize the CoreI2C driver with its base address, I2C serial address 
         // and serial clock divider.
       I2C_init( &g_i2c_inst, COREI2C_BASE_ADDR, I2C_SLAVE_SER_ADDR, I2C_PCLK_DIV_256 );
       
       // Specify the transmit buffer containing the data that will be returned to the 
       // master during read and write-read transactions.
       I2C_set_slave_tx_buffer( &g_i2c_inst, g_slave_tx_buffer, sizeof(g_slave_tx_buffer) );
    }
  @endcode
 */
void I2C_set_slave_tx_buffer
(
    i2c_instance_t * this_i2c,
    uint8_t * tx_buffer,
    uint16_t tx_size
);

/*-------------------------------------------------------------------------*//**
  I2C slave receive buffer configuration.
  ------------------------------------------------------------------------------
  This function specifies the memory buffer that will be used by the  I2C
  instance to receive data when it is a slave. This buffer is the memory where
  data will be stored when the  I2C is the target of an I2C master write
  transaction (i.e. when it is the slave).
  ------------------------------------------------------------------------------
  @param this_i2c:
    The this_i2c parameter is pointer to the i2c_instance_t data structure holding
    all data related to a specific CoreI2C channel. For example, if only one 
    channel is initialized, this data structure holds the information of channel 
    0 of the instantiated CoreI2C hardware.
  
  @param rx_buffer:
    This parameter is a pointer to the memory buffer allocated by the caller
    software to be used as a slave receive buffer.
  
  @param rx_size:
    Size of the slave receive buffer. This is the amount of memory that is
    allocated to the buffer pointed to by rx_buffer.
    Note:   This buffer size indirectly specifies the maximum I2C write
            transaction length this  I2C instance can be the target of. This
            is because this  I2C instance responds to further received
            bytes with a non-acknowledge bit (NACK) as soon as its receive
            buffer is full. This causes the write transaction to fail.
            
  @return none.  
      
  Example:
  @code
   #define COREI2C_BASE_ADDR  0xC0000000u
   #define I2C_SLAVE_SER_ADDR   0x10u

   i2c_instance_t g_i2c_inst;

   uint8_t g_slave_rx_buffer[SLAVE_RX_BUFFER_SIZE];

   void main( void )
   {
         // Initialize the CoreI2C driver with its base address, I2C serial address and 
         // serial clock divider.
       I2C_init( &g_i2c_inst, COREI2C_BASE_ADDR, I2C_SLAVE_SER_ADDR, I2C_PCLK_DIV_256 );
       
       // Specify the buffer used to store the data written by the I2C master.
       I2C_set_slave_rx_buffer( &g_i2c_inst, g_slave_rx_buffer, sizeof(g_slave_rx_buffer) );
    }
  @endcode
 */
void I2C_set_slave_rx_buffer
(
    i2c_instance_t * this_i2c,
    uint8_t * rx_buffer,
    uint16_t rx_size
);

/*-------------------------------------------------------------------------*//**
  I2C slave memory offset length configuration.
  ------------------------------------------------------------------------------
  This function is used as part of the configuration of a  I2C instance for
  operation as a slave supporting write-read transactions. It specifies the
  number of bytes expected as part of the write phase of a write-read
  transaction. The bytes received during the write phase of a write-read
  transaction will be interpreted as an offset into the slave's transmit buffer.
  This allows random access into the I2C slave transmit buffer from a remote
  I2C master.
  ------------------------------------------------------------------------------
  @param this_i2c:
    The this_i2c parameter is pointer to the i2c_instance_t data structure holding
    all data related to a specific CoreI2C channel. For example, if only one 
    channel is initialized, this data structure holds the information of channel 
    0 of the instantiated CoreI2C hardware.
  
  @param offset_length:
    The offset_length parameter configures the number of bytes to be interpreted
    by the  I2C slave as a memory offset value during the write phase of
    write-read transactions.
            
  @return none.  
      
  Example:
  @code
   #define COREI2C_BASE_ADDR  0xC0000000u
   #define I2C_SLAVE_SER_ADDR   0x10u
   #define SLAVE_TX_BUFFER_SIZE   10u

   i2c_instance_t g_i2c_inst;

   uint8_t g_slave_tx_buffer[SLAVE_TX_BUFFER_SIZE] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 ,10 } ;


   void main( void )
   {
         // Initialize the CoreI2C driver with its base address, I2C serial address 
         // and serial clock divider.
        I2C_init( &g_i2c_inst, COREI2C_BASE_ADDR, SLAVE_SER_ADDR, PCLK_DIV_256 );
        I2C_set_slave_tx_buffer( &g_i2c_inst, g_slave_tx_buffer, sizeof(g_slave_tx_buffer) );
        I2C_set_slave_mem_offset_length( &g_i2c_inst, 1 );
    }
  @endcode        
 */
void I2C_set_slave_mem_offset_length
(
    i2c_instance_t * this_i2c,
    uint8_t offset_length
);

/*-------------------------------------------------------------------------*//**
  I2C write handler registration. 
  ------------------------------------------------------------------------------
  Register the function that is called to process the data written to this
  I2C instance when it is the slave in an I2C write transaction.
  Note: The write handler is not called as a result of a write-read transaction.
        The write data of a write read transaction is interpreted as an offset
        into the slave's transmit buffer and handled by the driver.
  ------------------------------------------------------------------------------
  @param this_i2c:
    The this_i2c parameter is pointer to the i2c_instance_t data structure holding
    all data related to a specific CoreI2C channel. For example, if only one 
    channel is initialized, this data structure holds the information of channel 
    0 of the instantiated CoreI2C hardware.
  
  @param handler:
    Pointer to the function that will process the I2C write request.
            
  @return none.  
      
  Example:
  @code
   #define COREI2C_BASE_ADDR  0xC0000000u
   #define I2C_SLAVE_SER_ADDR   0x10u
   #define SLAVE_TX_BUFFER_SIZE   10u

   i2c_instance_t g_i2c_inst;

   uint8_t g_slave_tx_buffer[SLAVE_TX_BUFFER_SIZE] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 , 10 } ;

   // local function prototype
   void slave_write_handler
   (
       uint8_t * p_rx_data,
       uint16_t rx_size
   );
   
   void main( void )
   {
         // Initialize the CoreI2C driver with its base address, I2C serial address 
         // and serial clock divider.
        I2C_init( &g_i2c_inst, COREI2C_BASE_ADDR, SLAVE_SER_ADDR, PCLK_DIV_256 );
        I2C_set_slave_tx_buffer( &g_i2c_inst, g_slave_tx_buffer, sizeof(g_slave_tx_buffer) );
        I2C_set_slave_mem_offset_length( &g_i2c_inst, 1 );
        I2C_register_write_handler( &g_i2c_inst, (i2c_slave_wr_handler_t)slave_write_handler );
    }
  @endcode    
*/
void I2C_register_write_handler
(
    i2c_instance_t * this_i2c,
    i2c_slave_wr_handler_t handler
);

/*-------------------------------------------------------------------------*//**
  I2C slave enable.
  ------------------------------------------------------------------------------
  It enables the I2C instance identified through the this_i2c parameter for
  slave operation. The this_i2c instance is the target of an I2C write, read or
  write-read transaction. The I2C slave must call this function before being
  targeted for any transaction.
  ------------------------------------------------------------------------------
  @param this_i2c:
    The this_i2c parameter is pointer to the i2c_instance_t data structure holding
    all data related to a specific CoreI2C channel. For example, if only one
    channel is initialized, this data structure holds the information of channel
    0 of the instantiated CoreI2C hardware.

  @return none.

  Example:
  @code
   // Enable I2C slave
   I2C_enable_slave( &g_i2c_inst );
  @endcode
 */
void I2C_enable_slave
(
    i2c_instance_t * this_i2c
);

/*-------------------------------------------------------------------------*//**
  I2C slave disable.
  ------------------------------------------------------------------------------
  It disables the slave identified through the this_i2c parameter. The slave
  will not acknowledge for any transaction.
  ------------------------------------------------------------------------------
  @param this_i2c:
    The this_i2c parameter is pointer to the i2c_instance_t data structure holding
    all data related to a specific CoreI2C channel. For example, if only one
    channel is initialized, this data structure holds the information of channel
    0 of the instantiated CoreI2C hardware.

  @return none.

  Example:
  @code
   // Disable I2C slave
   I2C_disable_slave( &g_i2c_inst );
  @endcode
 */
void I2C_disable_slave
(
    i2c_instance_t * this_i2c
);
/*-------------------------------------------------------------------------*//**
  Set I2C slave second address.
  ------------------------------------------------------------------------------
  The function I2C_set_slave_second_addr() sets second slave address for the
  I2C slave device. This is secondary slave address which might be required
  in certain application to enable fail-safe mechanism in system design.
  As CoreI2C device supports 7-bit addressing, the highest value which can be
  assigned to second slave address is 127 (0x7F).
  Note: This version of the driver only supports CoreI2C hardware configured
        with a fixed second slave address.
  ------------------------------------------------------------------------------
  @param this_i2c:
    The this_i2c parameter is pointer to the i2c_instance_t data structure holding
    all data related to a specific CoreI2C channel. For example, if only one
    channel is initialized, this data structure holds the information of channel
    0 of the instantiated CoreI2C hardware.

  @param second_slave_addr:
    The second_slave_addr parameter is secondary slave address of the I2C device.

  @return
    none.

  Example:
  @code
   #define COREI2C_BASE_ADDR      0xC0000000u
   #define I2C_SLAVE_SER_ADDR     0x10u
   #define I2C_SECOND_SLAVE_ADDR  0x20u

   i2c_instance_t g_i2c_inst;
   void main( void )
   {
         // Initialize the CoreI2C driver with its base address, primary I2C serial 
         // address and serial clock divider.
        I2C_init( &g_i2c_inst, COREI2C_BASE_ADDR, SLAVE_SER_ADDR, PCLK_DIV_256 );
        I2C_set_slave_second_addr( &g_i2c_inst, I2C_SECOND_SLAVE_ADDR );
    }
  @endcode
 */
void I2C_set_slave_second_addr
(
    i2c_instance_t * this_i2c,
    uint8_t second_slave_addr
);

/*-------------------------------------------------------------------------*//**
  Disable second slave address.
  ------------------------------------------------------------------------------
  The function I2C_disable_slave_second_addr() disables second slave address of
  the I2C device. This is slave function and require only in case of specific
  application.
  Note: This version of the driver only supports CoreI2C hardware configured
        with a fixed second slave address.
  ------------------------------------------------------------------------------
  @param this_i2c:
    The this_i2c parameter is pointer to the i2c_instance_t data structure holding
    all data related to a specific CoreI2C channel. For example, if only one
    channel is initialized, this data structure holds the information of channel
    0 of the instantiated CoreI2C hardware.

  @return
    none.

  Example:
  @code
    i2c_instance_t g_i2c_inst;
    I2C_disable_slave_second_addr( &g_i2c_inst);
  @endcode
 */
void I2C_disable_slave_second_addr
(
    i2c_instance_t * this_i2c
);

/*-------------------------------------------------------------------------*//**
  The I2C_set_gca() function is used to set general call acknowledgment
  bit in slave device . This allows CoreI2C slave device to respond
  to general call or broadcast message from I2C master.

  @param this_i2c:
    The this_i2c parameter is pointer to the i2c_instance_t data structure holding
    all data related to a specific CoreI2C channel. For example, if only one
    channel is initialized, this data structure holds the information of channel
    0 of the instantiated CoreI2C hardware.

  @return
    none.

  Example:
  @code
    i2c_instance_t g_i2c_inst;
    I2C_set_gca(&g_i2c_inst); // Set general addressing call
  @endcode
 */
void I2C_set_gca
(
    i2c_instance_t * this_i2c
);

/*-------------------------------------------------------------------------*//**
  The I2C_clear_gca() function is used to clear the general call
  acknowledgment bit of the slave device. This will disallow the I2C slave
  device to respond to any general call from the master.

  @param this_i2c:
    The this_i2c parameter is pointer to the i2c_instance_t data structure holding
    all data related to a specific CoreI2C channel. For example, if only one
    channel is initialized, this data structure holds the information of channel
    0 of the instantiated CoreI2C hardware.

  @return
    none.

  Example:
  @code
    i2c_instance_t g_i2c_inst;

    I2C_enable_slave(&g_i2c_inst);
    I2C_clear_gca(&g_i2c_inst); // Clear General addressing call
  @endcode
 */

void I2C_clear_gca
(
    i2c_instance_t * this_i2c
);

/*------------------------------------------------------------------------------
                      I2C SMBUS specific APIs
 ----------------------------------------------------------------------------*/

/*-------------------------------------------------------------------------*//**
  The I2C_smbus_init() function enables SMBus timeouts and status logics.

  @param this_i2c:
    The this_i2c parameter is pointer to the i2c_instance_t data structure holding
    all data related to a specific CoreI2C channel. For example, if only one 
    channel is initialized, this data structure holds the information of channel 
    0 of the instantiated CoreI2C hardware.

  @return
    none.

  Example:
  @code
   #define COREI2C_BASE_ADDR  0xC0000000u
   #define COREI2C_SER_ADDR   0x10u

   i2c_instance_t g_i2c_inst;

   void system_init( void )
   {
        I2C_init( &g_i2c_inst, COREI2C_BASE_ADDR, COREI2C_SER_ADDR, I2C_PCLK_DIV_256 ) ;
        
        // Initialize SMBus feature
        I2C_smbus_init( &g_i2c_inst);
   }
  @endcode    
 */
void I2C_smbus_init
(
    i2c_instance_t * this_i2c
);

/*-------------------------------------------------------------------------*//**
  The I2C_enable_smbus_irq() function enables interrupts related to SMBus
  which can be either SMBSUS or SMBALERT interrupt.

  @param this_i2c:
    The this_i2c parameter is pointer to the i2c_instance_t data structure holding
    all data related to a specific CoreI2C channel. For example, if only one 
    channel is initialized, this data structure holds the information of channel 
    0 of the instantiated CoreI2C hardware.

  @param irq_type
    The irq_type specify the SMBUS interrupt which will be enabled.
    Following are two possible interrupts:
      I2C_SMBALERT_IRQ
      I2C_SMBSUS_IRQ

  @return
    none
  
  Example:
  @code
   #define COREI2C_BASE_ADDR  0xC0000000u
   #define COREI2C_SER_ADDR   0x10u

   i2c_instance_t g_i2c_inst;

   void main( void )
   {
        uint8_t irq_to_enable = I2C_SMBALERT_IRQ | I2C_SMBSUS_IRQ ; 
        I2C_init( &g_i2c_inst, COREI2C_BASE_ADDR, COREI2C_SER_ADDR, I2C_PCLK_DIV_256 ) ;
        
        // Initialize SMBus feature
        I2C_smbus_init( &g_i2c_inst);
        
        // Enable both I2C_SMBALERT_IRQ & I2C_SMBSUS_IRQ irq
        I2C_enable_smbus_irq( &g_i2c_inst, irq_to_enable );
   }
 */
void I2C_enable_smbus_irq
(
    i2c_instance_t * this_i2c,
    uint8_t  irq_type
);

/*-------------------------------------------------------------------------*//**
  The I2C_disable_smbus_irq() function disables interrupt related to SMBus
  which can be either SMBSUS or SMBALERT interrupt.

  @param this_i2c:
    The this_i2c parameter is pointer to the i2c_instance_t data structure holding
    all data related to a specific CoreI2C channel. For example, if only one 
    channel is initialized, this data structure holds the information of channel 
    0 of the instantiated CoreI2C hardware.

  @param irq_type
    The irq_type specify the SMBUS interrupt which will be disabled.
    Following are two possible interrupts:
      I2C_SMBALERT_IRQ
      I2C_SMBSUS_IRQ

  @return
    none.
      
  Example:
  @code
   #define COREI2C_BASE_ADDR  0xC0000000u
   #define COREI2C_SER_ADDR   0x10u

   i2c_instance_t g_i2c_inst;

   void main( void )
   {
        uint8_t irq_to_disable = I2C_SMBALERT_IRQ  ; 
        I2C_init( &g_i2c_inst, COREI2C_BASE_ADDR, COREI2C_SER_ADDR, I2C_PCLK_DIV_256 ) ;
        
        // Initialize SMBus feature
        I2C_smbus_init( &g_i2c_inst);
        
        // Enable only I2C_SMBALERT_IRQ
        I2C_disable_smbus_irq( &g_i2c_inst, irq_to_disable );
   }
 */
void I2C_disable_smbus_irq
(
    i2c_instance_t * this_i2c,
    uint8_t  irq_type
);

/*-------------------------------------------------------------------------*//**
  The function I2C_suspend_smbus_slave() forces slave devices into power down
  or suspend mode. This will be used by the I2C master.

  @param this_i2c:
    The this_i2c parameter is pointer to the i2c_instance_t data structure holding
    all data related to a specific CoreI2C channel. For example, if only one 
    channel is initialized, this data structure holds the information of channel 
    0 of the instantiated CoreI2C hardware.

  @return
    none.
    
  Example:
  @code
   #define COREI2C_BASE_ADDR  0xC0000000u
   #define COREI2C_SER_ADDR   0x10u

   i2c_instance_t g_i2c_inst;
   void main( void )
   {
        I2C_init( &g_i2c_inst, COREI2C_BASE_ADDR, COREI2C_SER_ADDR, I2C_PCLK_DIV_256 ) ;
        // Initialize SMBus feature
        I2C_smbus_init( &g_i2c_inst);
        // Suspend SMBus slave
        I2C_suspend_smbus_slave(&g_i2c_inst);
   }
  @endcode
 */
void I2C_suspend_smbus_slave
(
    i2c_instance_t * this_i2c
);

/*-------------------------------------------------------------------------*//**
  The I2C_reset_smbus() function resets the SMBus device.

  @param this_i2c:
    The this_i2c parameter is pointer to the i2c_instance_t data structure holding
    all data related to a specific CoreI2C channel. For example, if only one 
    channel is initialized, this data structure holds the information of channel 
    0 of the instantiated CoreI2C hardware.

  @return
    none.
  Example:
  @code
    i2c_instance_t g_i2c_inst;
    I2C_reset_smbus(&g_i2c_inst); 
  @endcode
 */
void I2C_reset_smbus
(
    i2c_instance_t * this_i2c
);

/*-------------------------------------------------------------------------*//**
  The I2C_set_smbus_alert() function is used to force master communication
  by I2C slave device. This function is used in the Slave mode.

  @param this_i2c:
    The this_i2c parameter is pointer to the i2c_instance_t data structure holding
    all data related to a specific CoreI2C channel. For example, if only one 
    channel is initialized, this data structure holds the information of channel 
    0 of the instantiated CoreI2C hardware.

  @return
    none.
  Example:
  @code
    i2c_instance_t g_i2c_inst;
    I2C_set_smbus_alert(&g_i2c_inst);
  @endcode
 */
void I2C_set_smbus_alert
(
    i2c_instance_t * this_i2c
);


/*-------------------------------------------------------------------------*//**
  The I2C_get_irq_status function returns the type of interrupt which is pending
  in CoreI2C module.
  The interrupts supported by CoreI2C are :
    1. INTR
    2. SMBUSALERT
    3. SMBSUS
  This function returns status of each I2C interrupts type in one byte data.
  Following are the bit positions associated with each interrupt type :
    Bit 0 - INTR IRQ
    Bit 1 - SMBUSALERT IRQ
    Bit 2 - SMBSUS IRQ
  It returns 0, if there are no pending interrupts.

  @param this_i2c:
    The this_i2c parameter is pointer to the i2c_instance_t data structure holding
    all data related to a specific CoreI2C channel. For example, if only one 
    channel is initialized, this data structure holds the information of channel 
    0 of the instantiated CoreI2C hardware.

  @returns
  This function returns status of each I2C interrupts type in one byte data.
  Following are the bit positions associated with each interrupt type :
    Bit 0 - INTR IRQ
    Bit 1 - SMBUSALERT IRQ
    Bit 2 - SMBSUS IRQ
  It returns 0, if there are no pending interrupts.

  Example
  @code

  @code
   #define COREI2C_BASE_ADDR  0xC0000000u
   #define COREI2C_SER_ADDR   0x10u

   i2c_instance_t g_i2c_inst;
   void main( void )
   {
        uint8_t irq_to_enable = I2C_SMBALERT_IRQ | I2C_SMBSUS_IRQ ;
        uint8_t pending_irq = 0u ;
        I2C_init( &g_i2c_inst, COREI2C_BASE_ADDR, COREI2C_SER_ADDR, I2C_PCLK_DIV_256 ) ;

        // Initialize SMBus feature
        I2C_smbus_init( &g_i2c_inst);

        // Enable both I2C_SMBALERT_IRQ & I2C_SMBSUS_IRQ irq
        I2C_enable_smbus_irq( &g_i2c_inst, irq_to_enable );

        // Get I2C IRQ type
        pending_irq = I2C_get_irq_status(&g_i2c_inst);

        // Let's assume, in system, INTR and SMBALERT IRQ is pending.
        // So pending_irq will return status of both the IRQs

        if( pending_irq & I2C_SMBALERT_IRQ )
        {
           // if true, it means SMBALERT_IRQ is there in pending IRQ list
        }
        if( pending_irq & I2C_INTR_IRQ )
        {
           // if true, it means I2C_INTR_IRQ is there in pending IRQ list
        }
   }
  @endcode
 */
uint8_t I2C_get_irq_status
(
    i2c_instance_t * this_i2c
);

/*-------------------------------------------------------------------------*//**
  The function I2C_set_user_data()sets user data.

  @param this_i2c:
    The this_i2c parameter is pointer to the i2c_instance_t data structure holding
    all data related to a specific CoreI2C channel. For example, if only one 
    channel is initialized, this data structure holds the information of channel 
    0 of the instantiated CoreI2C hardware.

  @param p_user_data:
    The p_user_data is pointer to void type.

  @return
    none.
 */
void I2C_set_user_data
(
    i2c_instance_t * this_i2c,
    void * p_user_data
);

/*-------------------------------------------------------------------------*//**
  The function I2C_get_user_data() function returns user data.

  @param this_i2c:
    The this_i2c parameter is pointer to the i2c_instance_t data structure holding
    all data related to a specific CoreI2C channel. For example, if only one 
    channel is initialized, this data structure holds the information of channel 
    0 of the instantiated CoreI2C hardware.

  @return
    This function returns user_data which is a pointer to void type.
 */
void * I2C_get_user_data
(
    i2c_instance_t * this_i2c
);

#ifdef __cplusplus
}
#endif

#endif
