Skip to content
Snippets Groups Projects
i2c.c 32.36 KiB
/* ****************************************************************************
 * Copyright (C) 2016 Maxim Integrated Products, Inc., All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
 * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 *
 * Except as contained in this notice, the name of Maxim Integrated
 * Products, Inc. shall not be used except as stated in the Maxim Integrated
 * Products, Inc. Branding Policy.
 *
 * The mere transfer of this software does not imply any licenses
 * of trade secrets, proprietary technology, copyrights, patents,
 * trademarks, maskwork rights, or any other form of intellectual
 * property whatsoever. Maxim Integrated Products, Inc. retains all
 * ownership rights.
 *
 * $Date: 2020-02-03 10:17:47 -0600 (Mon, 03 Feb 2020) $
 * $Revision: 51324 $
 *
 *************************************************************************** */


#include <stddef.h>
#include <stdint.h>
#include "mxc_config.h"
#include "mxc_assert.h"
#include "mxc_lock.h"
#include "mxc_sys.h"
#include "i2c.h"
#include <stdio.h>
#include "mxc_delay.h"

/* **** Definitions **** */
#define I2C_ERROR   (MXC_F_I2C_INT_FL0_ARB_ER | MXC_F_I2C_INT_FL0_TO_ER | MXC_F_I2C_INT_FL0_ADDR_NACK_ER | \
                    MXC_F_I2C_INT_FL0_DATA_ER | MXC_F_I2C_INT_FL0_DO_NOT_RESP_ER | MXC_F_I2C_INT_FL0_START_ER | \
                    MXC_F_I2C_INT_FL0_STOP_ER)
#define MASTER 1
#define SLAVE 0

/* For high speed mode, if the I2C bus capacitance is greater than 100pF, set this value to ((capacitance - 100) / 3).
   Otherwise leave it at 0. */
#define HS_SCALE_FACTOR         (0)

#define T_LOW_MIN     (160 + (160 * HS_SCALE_FACTOR / 100))   /* tLOW minimum in nanoseconds */
#define T_HIGH_MIN    (60 + (60 * HS_SCALE_FACTOR / 100))     /* tHIGH minimum in nanoseconds */
#define T_R_MAX_HS    (40 + (40 * HS_SCALE_FACTOR / 100))     /* tR maximum for high speed mode in nanoseconds */
#define T_F_MAX_HS    (40 + (40 * HS_SCALE_FACTOR / 100))     /* tF maximum for high speed mode in nanoseconds */
#define T_AF_MIN      (10 + (10 * HS_SCALE_FACTOR / 100))     /* tAF minimun in nanoseconds */

/* **** Variable Declaration **** */

// Saves the state of the non-blocking requests
typedef struct {
    i2c_req_t *req;
    i2c_state_t state;
    uint8_t num_wr;    // keep track of number of bytes loaded in the fifo during slave transmit
} i2c_req_state_t;
static i2c_req_state_t states[MXC_I2C_INSTANCES];

/* **** Function Prototypes **** */
static void I2C_MasterHandler(mxc_i2c_regs_t *i2c);
static void I2C_SlaveHandler(mxc_i2c_regs_t *i2c);
static void I2C_FreeCallback(int i2c_num, int error);
static void I2C_Recover(mxc_i2c_regs_t *i2c);

/* ************************************************************************** */
static int I2C_Setspeed(mxc_i2c_regs_t * i2c, i2c_speed_t i2cspeed)
{
    uint32_t ticks, ticks_lo, ticks_hi;

    if (i2cspeed == I2C_HS_MODE) {

        uint32_t sys_freq, tPCLK, targBusFreq, tSCLmin, cklMin, ckhMin, ckh_cklMin;

        /* Compute dividers for high speed mode. */
        sys_freq = SYS_I2C_GetFreq(i2c);
        MXC_ASSERT(sys_freq >= 1000);

        tPCLK = 1000000 / (sys_freq / 1000);
        MXC_ASSERT(tPCLK > 0)

        targBusFreq = i2cspeed - ((i2cspeed/2) * HS_SCALE_FACTOR / 100);
        if(targBusFreq < 1000) {
            return E_BAD_PARAM;
        }

        tSCLmin = 1000000 / (targBusFreq / 1000);                                
        cklMin = ((T_LOW_MIN + T_F_MAX_HS + (tPCLK - 1) - T_AF_MIN) / tPCLK) - 1;
        ckhMin = ((T_HIGH_MIN + T_R_MAX_HS + (tPCLK - 1) - T_AF_MIN) / tPCLK) - 1;
        ckh_cklMin = ((tSCLmin + (tPCLK - 1)) / tPCLK) - 2;

        ticks_lo = (cklMin > (ckh_cklMin - ckhMin)) ? (cklMin) : (ckh_cklMin - ckhMin);
        ticks_hi = ckhMin;

        if((ticks_lo > (MXC_F_I2C_HS_CLK_HS_CLK_LO >> MXC_F_I2C_HS_CLK_HS_CLK_LO_POS)) ||
           (ticks_hi > (MXC_F_I2C_HS_CLK_HS_CLK_HI >> MXC_F_I2C_HS_CLK_HS_CLK_HI_POS))) {
            return E_BAD_PARAM;
        }

        /* Write results to destination registers. */
        i2c->hs_clk = ((ticks_lo-1) << MXC_F_I2C_HS_CLK_HS_CLK_LO_POS) | (ticks_hi <<
                      MXC_F_I2C_HS_CLK_HS_CLK_HI_POS);

        /* Still need to load dividers for the preamble that each high-speed transaction starts with.
           Switch setting to fast mode and fall out of if statement. */
        i2cspeed = I2C_FAST_MODE;
    }
    
    /* Get the number of periph clocks needed to achieve selected speed. */
    ticks = SYS_I2C_GetFreq(i2c) / i2cspeed;

    /* For a 50% duty cycle, half the ticks will be spent high and half will be low. */
    ticks_hi = (ticks >> 1) - 1;
    ticks_lo = (ticks >> 1) - 1;

    /* Account for rounding error in odd tick counts. */
    if (ticks & 1) {
        ticks_hi++;
    }

    /* Will results fit into 9 bit registers?  (ticks_hi will always be >= ticks_lo.  No need to check ticks_lo.) */
    if (ticks_hi > 0x1FF) {
        return E_BAD_PARAM;
    }
    /* 0 is an invalid value for the destination registers. (ticks_hi will always be >= ticks_lo.  No need to check ticks_hi.) */
    if (ticks_lo == 0) {
        return E_BAD_PARAM;
    }

    /* Write results to destination registers. */
    i2c->clk_lo = ticks_lo;
    i2c->clk_hi = ticks_hi;

    return E_NO_ERROR;
}

/* ************************************************************************** */
int I2C_Init(mxc_i2c_regs_t *i2c, i2c_speed_t i2cspeed, const sys_cfg_i2c_t* sys_cfg)
{
    int err;
    int idx = MXC_I2C_GET_IDX(i2c);
    // Check the base pointer
    MXC_ASSERT(idx >= 0);

    // Set system level configurations
    if ((err = SYS_I2C_Init(i2c, sys_cfg)) != E_NO_ERROR) {
        return err;
    }

    // Always disable the HW autoflush on data NACK and let the SW handle the flushing.
    i2c->tx_ctrl0 |= 0x20;

    states[idx].num_wr = 0;

    i2c->ctrl = 0;   // clear configuration bits
    i2c->ctrl = MXC_F_I2C_CTRL_I2C_EN;       // Enable I2C
    i2c->master_ctrl = 0;  // clear master configuration bits
    i2c->status = 0;   // clear status bits

    /* If either SDA or SCL is already low, there is a problem.
     * Try reclaiming the bus by sending clocks until we have control of the SDA line.
     * Follow procedure defined in i2c spec.
     */
    if ((i2c->ctrl & (MXC_F_I2C_CTRL_SCL | MXC_F_I2C_CTRL_SDA)) !=
            (MXC_F_I2C_CTRL_SCL | MXC_F_I2C_CTRL_SDA)) {

        int i, have_control;

        // Set SCL/SDA as software controlled.
        i2c->ctrl |= MXC_F_I2C_CTRL_SW_OUT_EN;

        // Try to get control of SDA.
        for (i = 0; i < 16; i++) {
            have_control = 1;

            // Drive SCL low and check its state.
            i2c->ctrl &= ~(MXC_F_I2C_CTRL_SCL_OUT);
            mxc_delay(MXC_DELAY_USEC(5));
            if ((i2c->ctrl & MXC_F_I2C_CTRL_SCL) == MXC_F_I2C_CTRL_SCL) {
                have_control = 0;
            }

            // Drive SDA low and check its state.
            i2c->ctrl &= ~(MXC_F_I2C_CTRL_SDA_OUT);
            mxc_delay(MXC_DELAY_USEC(5));
            if ((i2c->ctrl & MXC_F_I2C_CTRL_SDA) == MXC_F_I2C_CTRL_SDA) {
                have_control = 0;
            }

            // Release SDA and check its state.
            i2c->ctrl |= (MXC_F_I2C_CTRL_SDA_OUT);
            mxc_delay(MXC_DELAY_USEC(5));
            if ((i2c->ctrl & MXC_F_I2C_CTRL_SDA) != MXC_F_I2C_CTRL_SDA) {
                have_control = 0;
            }

            // Release SCL and check its state.
            i2c->ctrl |= (MXC_F_I2C_CTRL_SCL_OUT);
            mxc_delay(MXC_DELAY_USEC(5));
            if ((i2c->ctrl & MXC_F_I2C_CTRL_SCL) != MXC_F_I2C_CTRL_SCL) {
                have_control = 0;
            }

            if (have_control) {
                // Issue stop
                // Drive SDA low.
                i2c->ctrl &= ~(MXC_F_I2C_CTRL_SDA_OUT);
                mxc_delay(MXC_DELAY_USEC(5));
                // Release SDA.
                i2c->ctrl |= (MXC_F_I2C_CTRL_SDA_OUT);
                mxc_delay(MXC_DELAY_USEC(5));
                break;
            }
        }

        if (!have_control) {
            return E_COMM_ERR;
        }
    }

    i2c->ctrl = 0;   // clear configuration bits
    i2c->ctrl = MXC_F_I2C_CTRL_I2C_EN;       // Enable I2C
    i2c->master_ctrl = 0;  // clear master configuration bits
    i2c->status= 0;   // clear status bits

    // Check for HS mode
    if (i2cspeed == I2C_HS_MODE) {
        i2c->ctrl |= MXC_F_I2C_CTRL_HS_MODE;    // Enable HS mode
    }

    // Disable and clear interrupts
    i2c->int_en0 = 0;
    i2c->int_en1 = 0;
    i2c->int_fl0 = i2c->int_fl0;
    i2c->int_fl1 = i2c->int_fl1;

    i2c->timeout = 0x0;   // set timeout
    i2c->rx_ctrl0 |= MXC_F_I2C_RX_CTRL0_RX_FLUSH; // clear the RX FIFO
    i2c->tx_ctrl0 |= MXC_F_I2C_TX_CTRL0_TX_FLUSH; // clear the TX FIFO

    return I2C_Setspeed(i2c, i2cspeed);
}
/* ************************************************************************** */
int I2C_Shutdown(mxc_i2c_regs_t *i2c)
{
    int i2c_num, err;

    // Check the base pointer
    i2c_num = MXC_I2C_GET_IDX(i2c);
    MXC_ASSERT(i2c_num >= 0);

    // Disable and clear interrupts
    i2c->int_en0 = 0;
    i2c->int_en1 = 0;
    i2c->int_fl0 = i2c->int_fl0;
    i2c->int_fl1 = i2c->int_fl1;

    i2c->rx_ctrl0 |= MXC_F_I2C_RX_CTRL0_RX_FLUSH; // clear the RX FIFO
    i2c->tx_ctrl0 |= MXC_F_I2C_TX_CTRL0_TX_FLUSH; // clear the TX FIFO

    // Call all of the pending callbacks for this I2C
    if (states[i2c_num].req != NULL) {
        I2C_Recover(i2c);
        I2C_FreeCallback(i2c_num, E_SHUTDOWN);
    }

    i2c->ctrl = 0;
    
    // Clears system level configurations
    if ((err = SYS_I2C_Shutdown(i2c)) != E_NO_ERROR) {
        return err;
    }

    return E_NO_ERROR;
}

/* ************************************************************************** */
int I2C_MasterWrite(mxc_i2c_regs_t *i2c, uint8_t addr, const uint8_t* data, int len, int restart)
{
    int save_len = len;

    if (len == 0) {
        return E_NO_ERROR;
    }

    // Clear the lock out bit (W1C) in case it is set.
    i2c->int_fl0 = MXC_F_I2C_INT_FL0_TX_LOCK_OUT;
    i2c->int_fl0 = i2c->int_fl0;

    // Enable master mode
    i2c->ctrl |= MXC_F_I2C_CTRL_MST;

    // Load FIFO with slave address for WRITE and as much data as we can
    while (i2c->status & MXC_F_I2C_STATUS_TX_FULL) {}
    
    i2c->fifo = addr & ~(0x1);

    while ((len > 0) && !(i2c->status & MXC_F_I2C_STATUS_TX_FULL)) {
        i2c->fifo = *data++;
        len--;
    }

    i2c->master_ctrl |= MXC_F_I2C_MASTER_CTRL_START;

    // Write remaining data to FIFO
    while (len > 0) {

        // Check for errors
        if (i2c->int_fl0 & I2C_ERROR) {
            // Set the stop bit
            i2c->master_ctrl &= ~(MXC_F_I2C_MASTER_CTRL_RESTART);
            i2c->master_ctrl |= MXC_F_I2C_MASTER_CTRL_STOP;
             while (!(i2c->int_fl0 & (MXC_F_I2C_INT_FL0_STOP))) {}    

            return E_COMM_ERR;
        }

        if (!(i2c->status & MXC_F_I2C_STATUS_TX_FULL)) {
            i2c->fifo = *data++;
            len--;
        }
    }

    if (restart)  {
        i2c->master_ctrl |= MXC_F_I2C_MASTER_CTRL_RESTART;
    } else {
        i2c->master_ctrl |= MXC_F_I2C_MASTER_CTRL_STOP;
    }

    // Wait for Done
    while (!(i2c->int_fl0 & (MXC_F_I2C_INT_FL0_DONE | I2C_ERROR))) {}    

    i2c->int_fl0 = MXC_F_I2C_INT_FL0_DONE;
    // Wait for Stop
    if (!restart) {
        while (!(i2c->int_fl0 & (MXC_F_I2C_INT_FL0_STOP ))) {}
        
        i2c->int_fl0 = MXC_F_I2C_INT_FL0_STOP;
    }

    // Check for errors
    if (i2c->int_fl0 & I2C_ERROR) {
        return E_COMM_ERR;
    }

    return save_len;
}

/* ************************************************************************** */
int I2C_MasterRead(mxc_i2c_regs_t *i2c, uint8_t addr, uint8_t* data, int len, int restart)
{
    int save_len = len;

    if (len == 0) {
        return E_NO_ERROR;
    }

    if (len > 256) {
        return E_BAD_PARAM;
    }

    i2c->int_fl0 = MXC_F_I2C_INT_FL0_TX_LOCK_OUT;
    i2c->int_fl0 = i2c->int_fl0;

    // Make sure the I2C has been initialized
    if (!(i2c->ctrl & MXC_F_I2C_CTRL_I2C_EN)) {
        return E_UNINITIALIZED;
    }

    // Enable master mode
    i2c->ctrl |= MXC_F_I2C_CTRL_MST;

    // Set receive count
    i2c->rx_ctrl1= len;

    i2c->master_ctrl |= MXC_F_I2C_MASTER_CTRL_START;

    // Load FIFO with slave address
    while (i2c->status & MXC_F_I2C_STATUS_TX_FULL) {}
    i2c->fifo = (addr | 1);


    // Wait for all data to be received or error
    while (len > 0) {

        // Check for errors
        if (i2c->int_fl0 & I2C_ERROR) {
            // Set the stop bit
            i2c->master_ctrl |= MXC_F_I2C_MASTER_CTRL_STOP;
            while (!(i2c->int_fl0 & (MXC_F_I2C_INT_FL0_STOP))) {}
            return E_COMM_ERR;
        }

        if (!(i2c->status & MXC_F_I2C_STATUS_RX_EMPTY)) {
            *data++ = i2c->fifo;
            len--;
        }
    }

    if (i2c->int_fl0 & I2C_ERROR) {
        // Set the stop bit
        i2c->master_ctrl |= MXC_F_I2C_MASTER_CTRL_STOP;
        while (!(i2c->int_fl0 & (MXC_F_I2C_INT_FL0_STOP))) {}
        return E_COMM_ERR;
    }

    if (restart)  {
        i2c->master_ctrl |= MXC_F_I2C_MASTER_CTRL_RESTART;
    } else {
        i2c->master_ctrl |= MXC_F_I2C_MASTER_CTRL_STOP;
    }

    // Wait for Done
    while (!(i2c->int_fl0 & (MXC_F_I2C_INT_FL0_DONE | I2C_ERROR))) {
        
    }
    
    i2c->int_fl0 = MXC_F_I2C_INT_FL0_DONE;

    // Wait for Stop
    if (!restart) {
        while (!(i2c->int_fl0 &( MXC_F_I2C_INT_FL0_STOP | I2C_ERROR))) {
            
        }
        
        i2c->int_fl0 = MXC_F_I2C_INT_FL0_STOP;
    }

    // Check for errors
    if (i2c->int_fl0 & I2C_ERROR) {
        return E_COMM_ERR;
    }

    return save_len;
}

/* ************************************************************************** */
int I2C_Slave(mxc_i2c_regs_t *i2c, uint8_t addr, const uint8_t* read_data, int read_len,
              uint8_t* write_data, int write_len, int* tx_num, int* rx_num,
              i2c_autoflush_disable_t sw_autoflush_disable)
{
    int i2c_num;

    i2c_num = MXC_I2C_GET_IDX(i2c);
    if ((read_data == NULL) && (write_data == NULL)) {
        return E_NULL_PTR;
    }

    // Make sure the I2C has been initialized
    if (!(i2c->ctrl & MXC_F_I2C_CTRL_I2C_EN)) {
        return E_UNINITIALIZED;
    }

    if ((read_len == 0) && (write_len == 0)) {
        return E_NO_ERROR;
    }

    if (mxc_get_lock((uint32_t*)&states[i2c_num].req, 1) != E_NO_ERROR) {
        return E_BUSY;
    }
    // Disable master mode
    i2c->ctrl &= ~MXC_F_I2C_CTRL_MST;

    // Clear any previous errors
    i2c->int_fl0 = i2c->int_fl0;
    i2c->int_fl1 = i2c->int_fl1;

    // Set the slave address
    i2c->slave_addr = (addr >> 1);

    // Wait for address match
    while (!(i2c->int_fl0 & MXC_F_I2C_INT_FL0_ADDR_MATCH) && !(i2c->int_fl0 & I2C_ERROR)) {
                
    }
    
    i2c->int_fl0 = MXC_F_I2C_INT_FL0_ADDR_MATCH;
    i2c->int_fl0 = MXC_F_I2C_INT_FL0_TX_LOCK_OUT;

    if (i2c->int_fl0 & I2C_ERROR) {
        if (!sw_autoflush_disable) {
            i2c->tx_ctrl0 |= MXC_F_I2C_TX_CTRL0_TX_FLUSH;
            i2c->rx_ctrl0 |= MXC_F_I2C_RX_CTRL0_RX_FLUSH;
        }
        mxc_free_lock((uint32_t*)&states[i2c_num]);
        return E_COMM_ERR;
    }

    // See if we're reading or writing
    if (i2c->ctrl & MXC_F_I2C_CTRL_READ) {
        // This is the master read/slave write case
        if (read_data == NULL || read_len == 0) {
            mxc_free_lock((uint32_t*)&states[i2c_num]);
            return E_NULL_PTR;
        }

        // Wait for all data to be received or error
        while (read_len > 0) {

            // Check for errors
            if (i2c->int_fl0 & I2C_ERROR) {
                *tx_num = states[i2c_num].num_wr - ((i2c->tx_ctrl1 & MXC_F_I2C_TX_CTRL1_TX_FIFO) >> MXC_F_I2C_TX_CTRL1_TX_FIFO_POS);
                states[i2c_num].num_wr = 0;
                if (!sw_autoflush_disable) {
                    i2c->tx_ctrl0 |= MXC_F_I2C_TX_CTRL0_TX_FLUSH;
                }
                mxc_free_lock((uint32_t*)&states[i2c_num]);
                return E_COMM_ERR;
            }

            // Check for nack from master
            if (i2c->int_fl0 & MXC_F_I2C_INT_FL0_TX_LOCK_OUT) {
                break;
            }

            // Check for done bit
            if (i2c->int_fl0 & MXC_F_I2C_INT_FL0_DONE) {
                break;
            }

            if (!(i2c->status & MXC_F_I2C_STATUS_TX_FULL)) {
                i2c->fifo = *read_data++;
                states[i2c_num].num_wr++;
                read_len--;
            }
        }

        // Wait for Done
        while (!(i2c->int_fl0 & MXC_F_I2C_INT_FL0_DONE)) {}

        // Calculate number of bytes sent by the slave
        *tx_num = states[i2c_num].num_wr - ((i2c->tx_ctrl1 & MXC_F_I2C_TX_CTRL1_TX_FIFO) >> MXC_F_I2C_TX_CTRL1_TX_FIFO_POS);
        states[i2c_num].num_wr = 0;
        if (!sw_autoflush_disable) {
            // Flush the TX FIFO
            i2c->tx_ctrl0 |= MXC_F_I2C_TX_CTRL0_TX_FLUSH;
        }

    } else {
        // This is the master write/slave read case
        if (write_data == NULL || write_len == 0) {
            mxc_free_lock((uint32_t*)&states[i2c_num]);
            return E_NULL_PTR;
        }

        // Wait for all data to be written or error
        while (write_len > 0) {

            // Check for errors
            if (i2c->int_fl0 & I2C_ERROR) {
                if (!sw_autoflush_disable) {
                    i2c->rx_ctrl0 |= MXC_F_I2C_RX_CTRL0_RX_FLUSH;
                }
                mxc_free_lock((uint32_t*)&states[i2c_num]);
                return E_COMM_ERR;
            }

            // Check for done bit
            if (i2c->int_fl0 & MXC_F_I2C_INT_FL0_DONE) {
                break;
            }

            if (!(i2c->status & MXC_F_I2C_STATUS_RX_EMPTY)) {
                *write_data++ = i2c->fifo;
                (*rx_num)++;
                write_len--;
            }
        }

        // Wait for done
        while (!(i2c->int_fl0 & MXC_F_I2C_INT_FL0_DONE)) {
            
        }
        // Flush the FIFO
        if (!sw_autoflush_disable) {
            i2c->rx_ctrl0 |= MXC_F_I2C_RX_CTRL0_RX_FLUSH;
        }
    }

    // Check for errors
    if (i2c->int_fl0 & I2C_ERROR) {
        // Flush the FIFO
        if (!sw_autoflush_disable) {
            i2c->tx_ctrl0 |= MXC_F_I2C_TX_CTRL0_TX_FLUSH;
            i2c->rx_ctrl0 |= MXC_F_I2C_RX_CTRL0_RX_FLUSH;
        }
        mxc_free_lock((uint32_t*)&states[i2c_num]);
        return E_COMM_ERR;
    }
    mxc_free_lock((uint32_t*)&states[i2c_num]);
    return E_NO_ERROR;
}

/* ************************************************************************** */
int I2C_MasterAsync(mxc_i2c_regs_t *i2c, i2c_req_t *req)
{
    int i2c_num;

    i2c_num = MXC_I2C_GET_IDX(i2c);
    if (req->state == I2C_STATE_READING) {
        // Check the parameters
        if (req->rx_len == 0) {
            return E_NO_ERROR;
        }
        if (req->rx_data == NULL) {
            return E_NULL_PTR;
        }

    } else {
        // Check the parameters
        if (req->tx_len == 0) {
            return E_NO_ERROR;
        }
        if (req->tx_data == NULL) {
            return E_NULL_PTR;
        }
    }

    // Make sure the I2C has been initialized
    if (!(i2c->ctrl & MXC_F_I2C_CTRL_I2C_EN)) {
        return E_UNINITIALIZED;
    }

    // Attempt to register this request
    if (mxc_get_lock((uint32_t*)&states[i2c_num].req, (uint32_t)req) != E_NO_ERROR) {
        return E_BUSY;
    }
    states[i2c_num].state = req->state;
    states[i2c_num].req = req;

    // Enable master mode
    i2c->ctrl |= MXC_F_I2C_CTRL_MST;

    // Clear the byte counters
    req->tx_num = 0;
    req->rx_num = 0;

    // Disable and clear the interrupts
    i2c->int_en0 = 0;
    i2c->int_en1 = 0;
    i2c->int_fl0 = i2c->int_fl0;
    i2c->int_fl1 = i2c->int_fl1;

    // Start the transaction
    I2C_MasterHandler(i2c);
    return E_NO_ERROR;
}


/* ************************************************************************** */
static void I2C_MasterHandler(mxc_i2c_regs_t *i2c)
{

    uint32_t int0, inten0 = 0;
    int i2c_num;
    unsigned rx_remain, tx_remain;
    i2c_req_t *req;

    i2c_num = MXC_I2C_GET_IDX(i2c);
    req = states[i2c_num].req;

    // Check for errors
    int0 = i2c->int_fl0;
    if (int0 & I2C_ERROR) {

        // Set the stop bit
        i2c->master_ctrl &= ~(MXC_F_I2C_MASTER_CTRL_RESTART);
        i2c->master_ctrl |= MXC_F_I2C_MASTER_CTRL_STOP;

        i2c->int_en0 = 0;
        if (req->callback != NULL) {
            I2C_Recover(i2c);
            I2C_FreeCallback(i2c_num, E_COMM_ERR);
        }
        return;
    }

    rx_remain = req->rx_len - req->rx_num;
    tx_remain = req->tx_len - req->tx_num;

    if (states[i2c_num].state == I2C_STATE_READING) {


        // Read out any data in the RX FIFO
        while (rx_remain && !(i2c->status & MXC_F_I2C_STATUS_RX_EMPTY)) {
            *(req->rx_data)++ = i2c->fifo;
            req->rx_num++;
            rx_remain--;
        }

        // Load the slave address if we haven't already started reading the data
        if (rx_remain == req->rx_len) {
            i2c->fifo = (req->addr | 1);

            // Set the RX Count
            i2c->rx_ctrl1 = req->rx_len;

            // Start transmission if idle
            if (!(i2c->status & MXC_F_I2C_STATUS_BUS)) {
                i2c->master_ctrl |= MXC_F_I2C_MASTER_CTRL_START;
            }

            // Set restart or stop
            if (req->restart)  {
                i2c->master_ctrl |= MXC_F_I2C_MASTER_CTRL_RESTART;
            } else {
                i2c->master_ctrl |= MXC_F_I2C_MASTER_CTRL_STOP;
                inten0 |= MXC_F_I2C_INT_EN0_STOP;
            }
        }

        // Set the RX threshold interrupt level
        if (rx_remain >= (MXC_I2C_FIFO_DEPTH - 1)) {
            i2c->rx_ctrl0 = ((i2c->rx_ctrl0 & ~(MXC_F_I2C_RX_CTRL0_RX_THRESH)) |
                             (MXC_I2C_FIFO_DEPTH - 1) << MXC_F_I2C_RX_CTRL0_RX_THRESH_POS);

            inten0 |= MXC_F_I2C_INT_EN0_RX_THRESH;
        }

    } else {

        // Load the slave address if we haven't already started writing the data
        if (tx_remain == req->tx_len) {
            i2c->fifo = req->addr;
            // Start transmission if idle
            if (!(i2c->status &  MXC_F_I2C_STATUS_BUS)) {
                i2c->master_ctrl |= MXC_F_I2C_MASTER_CTRL_START;
            }
        }

        // Fill the FIFO
        while ((tx_remain > 0) && !(i2c->status & MXC_F_I2C_STATUS_TX_FULL)) {
            i2c->fifo = *(req->tx_data)++;
            req->tx_num++;
            tx_remain--;
        }

        // Set the TX threshold interrupt level, or restart/stop
        if (tx_remain) {
            i2c->tx_ctrl0 = ((i2c->tx_ctrl0 & ~(MXC_F_I2C_TX_CTRL0_TX_THRESH)) | (1 << MXC_F_I2C_TX_CTRL0_TX_THRESH_POS));
            inten0 |= MXC_F_I2C_INT_EN0_TX_THRESH;
        }
        // Set restart or stop if at the end of the transaction since these actions happen at the moment the bit is set.
        else if (req->restart)  {
            i2c->master_ctrl |= MXC_F_I2C_MASTER_CTRL_RESTART;
        } else {
            i2c->master_ctrl |= MXC_F_I2C_MASTER_CTRL_STOP;
            inten0 |= MXC_F_I2C_INT_EN0_STOP;
        }
    }

    if (req->restart) {
        //  Check for DONE interrupt
        if ((int0 & MXC_F_I2C_INT_FL0_DONE)) {
            // Read out any data in the RX FIFO
            while (rx_remain && !(i2c->status & MXC_F_I2C_STATUS_RX_EMPTY)) {
                *(req->rx_data)++ = i2c->fifo;
                req->rx_num++;
                rx_remain--;
            }
            i2c->int_en0 = 0;
            if (req->callback != NULL) {
                I2C_Recover(i2c);
                I2C_FreeCallback(i2c_num, E_NO_ERROR);
            }
            return;
        }
    } else {
        // Check for STOP interrupt
        if ((int0 & MXC_F_I2C_INT_FL0_STOP)) {
            i2c->int_en0 = 0;
            if (req->callback != NULL) {
                I2C_Recover(i2c);
                I2C_FreeCallback(i2c_num, E_NO_ERROR);
            }

            return;
        }

        // Check for DONE interrupt
        if ((int0 & MXC_F_I2C_INT_FL0_DONE)) {
            // Read out any data in the RX FIFO
            while (rx_remain && !(i2c->status & MXC_F_I2C_STATUS_RX_EMPTY)) {
                *(req->rx_data)++ = i2c->fifo;
                req->rx_num++;
                rx_remain--;
            }

            return;
        }
    }

    // Clear the interrupts
    i2c->int_fl0 = int0;
    inten0 |= (MXC_F_I2C_INT_EN0_DONE | I2C_ERROR);
    i2c->int_en0 = inten0;
}

/* ************************************************************************** */
int I2C_SlaveAsync(mxc_i2c_regs_t *i2c, i2c_req_t *req)
{
    int i2c_num;

    i2c_num = MXC_I2C_GET_IDX(i2c);

    // Make sure the I2C has been initialized
    if (!(i2c->ctrl & MXC_F_I2C_CTRL_I2C_EN)) {
        return E_UNINITIALIZED;
    }

    // Attempt to register this request
    if (mxc_get_lock((uint32_t*)&states[i2c_num].req, (uint32_t)req) != E_NO_ERROR) {
        return E_BUSY;
    }

    states[i2c_num].req = req;

    // Set Slave Address
    i2c->slave_addr = (req->addr >> 1);

    // Clear the byte counters
    req->tx_num = 0;
    req->rx_num = 0;
    // Disable and clear the interrupts
    i2c->int_en0 = 0;
    i2c->int_en1 = 0;
    i2c->int_fl0 = i2c->int_fl0;
    i2c->int_fl1 = i2c->int_fl1;
    i2c->int_en0 |= MXC_F_I2C_INT_EN0_ADDR_MATCH;

    return E_NO_ERROR;
}
/* ************************************************************************** */
static void I2C_SlaveHandler(mxc_i2c_regs_t *i2c)
{
    uint32_t int0, inten0 = 0;
    int rx_remain, tx_remain, i2c_num;
    i2c_req_t *req;

    i2c_num = MXC_I2C_GET_IDX(i2c);
    req = states[i2c_num].req;

    if ( i2c->int_fl0 & MXC_F_I2C_INT_FL0_ADDR_MATCH ) {
        i2c->int_fl0 |=MXC_F_I2C_INT_EN0_STOP;
    }

    // Check for errors
    int0 = i2c->int_fl0;
    if (int0 & I2C_ERROR) {
        i2c->int_en0 = 0;
        // Calculate the number of bytes sent by the slave
        req->tx_num = states[i2c_num].num_wr - ((i2c->tx_ctrl1 & MXC_F_I2C_TX_CTRL1_TX_FIFO) >> MXC_F_I2C_TX_CTRL1_TX_FIFO_POS);

        if (!req->sw_autoflush_disable) {
            // Manually clear the TXFIFO
            i2c->tx_ctrl0 |= MXC_F_I2C_TX_CTRL0_TX_FLUSH;
        }
        states[i2c_num].num_wr = 0;
        if (req->callback != NULL) {
            I2C_Recover(i2c);
            I2C_FreeCallback(i2c_num, E_COMM_ERR);
        }
        return;
    }

    rx_remain = req->rx_len - req->rx_num;
    tx_remain = req->tx_len - states[i2c_num].num_wr;

    //Check if Master Write has been called and if there is a rx_data buffer
    if ((i2c->int_fl0 & MXC_F_I2C_INT_FL0_TX_LOCK_OUT) && !(i2c->ctrl & MXC_F_I2C_CTRL_READ)) {

        i2c->int_en0 = 0;
        if (req->rx_data == NULL) {
            I2C_Recover(i2c);
            I2C_FreeCallback(i2c_num, E_NULL_PTR);
            return;
        }
    }

    // Check for DONE interrupt
    if (int0 & MXC_F_I2C_INT_EN0_DONE) {
        // Read out any data in the RX FIFO
        while (rx_remain && !(i2c->status & MXC_F_I2C_STATUS_RX_EMPTY)) {
            *(req->rx_data)++ = i2c->fifo;
            req->rx_num++;
            rx_remain--;
        }

        // Calculate the number of bytes sent by the slave
        req->tx_num = states[i2c_num].num_wr - ((i2c->tx_ctrl1 & MXC_F_I2C_TX_CTRL1_TX_FIFO) >> MXC_F_I2C_TX_CTRL1_TX_FIFO_POS);

        if (!req->sw_autoflush_disable) {
            // Manually clear the TXFIFO
            i2c->tx_ctrl0 |= MXC_F_I2C_TX_CTRL0_TX_FLUSH;
        }
        states[i2c_num].num_wr = 0;
        i2c->int_en0 = 0;
        if (req->callback != NULL) {
            if (i2c->int_fl0 & MXC_F_I2C_INT_FL0_STOP) {
                I2C_Recover(i2c);
            } else {    
                i2c->int_fl0 = i2c->int_fl0;
                i2c->int_fl1 = i2c->int_fl1;
            }
            I2C_FreeCallback(i2c_num, E_NO_ERROR);
        }
        return;
    }

    // Clear the interrupts
    i2c->int_fl0 = int0;
    if (i2c->ctrl & MXC_F_I2C_CTRL_READ) {

        i2c->int_en0 = 0;
        if (req->tx_data == NULL) {
            I2C_Recover(i2c);
            I2C_FreeCallback(i2c_num, E_NULL_PTR);
            return;
        }
        // Fill the FIFO
        while ((tx_remain > 0) && !(i2c->status & MXC_F_I2C_STATUS_TX_FULL)) {
            i2c->fifo = *(req->tx_data)++;
            states[i2c_num].num_wr++;
            tx_remain--;
        }

        // Set the TX threshold interrupt level
        if (tx_remain) {
            i2c->tx_ctrl0 = ((i2c->tx_ctrl0 & ~(MXC_F_I2C_TX_CTRL0_TX_THRESH)) | (1 << MXC_F_I2C_TX_CTRL0_TX_THRESH_POS));
            inten0 |= MXC_F_I2C_INT_EN0_TX_THRESH;
        }

    } else {
        // Read out any data in the RX FIFO
        while (rx_remain && !(i2c->status & MXC_F_I2C_STATUS_RX_EMPTY)) {
            *(req->rx_data)++ = i2c->fifo;
            req->rx_num++;
            rx_remain--;
        }

        // Set the RX threshold interrupt level
        if (rx_remain >= (MXC_I2C_FIFO_DEPTH - 1)) {
            i2c->rx_ctrl0 = ((i2c->rx_ctrl0 & ~(MXC_F_I2C_RX_CTRL0_RX_THRESH)) |
                             (MXC_I2C_FIFO_DEPTH - 1) << MXC_F_I2C_RX_CTRL0_RX_THRESH_POS);

            inten0 |= MXC_F_I2C_INT_EN0_RX_THRESH;
        }else{
            i2c->rx_ctrl0 = ((i2c->rx_ctrl0 & ~(MXC_F_I2C_RX_CTRL0_RX_THRESH)) |
                 (rx_remain) << MXC_F_I2C_RX_CTRL0_RX_THRESH_POS);

            inten0 |= MXC_F_I2C_INT_EN0_RX_THRESH;
        }

    }
    inten0 |= (MXC_F_I2C_INT_EN0_DONE | I2C_ERROR | MXC_F_I2C_INT_EN0_TX_LOCK_OUT);
    i2c->int_en0 = inten0;
}

/* ************************************************************************** */
void I2C_Handler(mxc_i2c_regs_t *i2c)
{
    if (i2c->ctrl & MXC_F_I2C_CTRL_MST && i2c->int_fl0) {
        // Service master interrupts if we're in master mode
        I2C_MasterHandler(i2c);
    } else if (i2c->int_fl0 || i2c->int_fl1) {
        // Service the slave interrupts
        I2C_SlaveHandler(i2c);
    }
}

/* ************************************************************************** */
void I2C_DrainRX(mxc_i2c_regs_t *i2c)
{
    i2c->rx_ctrl0 |= MXC_F_I2C_RX_CTRL0_RX_FLUSH;
}

/* ************************************************************************** */
void I2C_DrainTX(mxc_i2c_regs_t *i2c)
{
    i2c->tx_ctrl0 |= MXC_F_I2C_TX_CTRL0_TX_FLUSH;
}
/* ************************************************************************* */
static void I2C_FreeCallback(int i2c_num, int error)
{
    // Save the request
    i2c_req_t *temp_req = states[i2c_num].req;

    mxc_free_lock((uint32_t*)&states[i2c_num].req);

    // Callback if not NULL
    if (temp_req->callback != NULL) {
        temp_req->callback(temp_req, error);
    }
}

/* ************************************************************************* */
static void I2C_Recover(mxc_i2c_regs_t *i2c)
{
    // Disable and clear interrupts
    i2c->int_en0 = 0;
    i2c->int_en1 = 0;
    i2c->int_fl0 = i2c->int_fl0;
    i2c->int_fl1 = i2c->int_fl1;
    i2c->ctrl = 0;
    i2c->ctrl = MXC_F_I2C_CTRL_I2C_EN;
}

/* ************************************************************************* */
int I2C_AbortAsync(i2c_req_t *req)
{
    int i2c_num;
    mxc_i2c_regs_t *i2c;

    // Find the request, set to NULL
    for (i2c_num = 0; i2c_num < MXC_I2C_INSTANCES; i2c_num++) {
        if (req == states[i2c_num].req) {

            i2c = MXC_I2C_GET_I2C(i2c_num);
            I2C_Recover(i2c);
            I2C_FreeCallback(i2c_num, E_ABORT);

            return E_NO_ERROR;
        }
    }

    return E_BAD_PARAM;
}

/* ************************************************************************* */
int I2C_SetTimeout(mxc_i2c_regs_t *i2c, int us){
    uint32_t timeout;
    timeout = (PeripheralClock/1000000) * us;
    if(timeout > 0xFFFF){
        return E_BAD_PARAM;
    }
    i2c->timeout = timeout;
    return E_NO_ERROR;
}

/* ************************************************************************* */
void I2C_ClearTimeout (mxc_i2c_regs_t *i2c)
{
    i2c->timeout = 0;
}