t

Fork of mbed-dev by mbed official

Revision:
157:ff67d9f36b67
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/targets/TARGET_Maxim/TARGET_MAX32630/mxc/i2cm.c	Thu Feb 02 17:01:33 2017 +0000
@@ -0,0 +1,924 @@
+/**
+ * @file
+ * @brief      This file contains the function implementations for the I2CM 
+ *             (Inter-Integrated Circuit Master) peripheral module.
+ */
+
+/* ****************************************************************************
+ * 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: 2016-09-09 11:40:02 -0500 (Fri, 09 Sep 2016) $
+ * $Revision: 24336 $
+ *
+ *************************************************************************** */
+
+/* **** Includes **** */
+#include <string.h>
+#include "mxc_assert.h"
+#include "mxc_lock.h"
+#include "mxc_errors.h"
+#include "mxc_sys.h"
+#include "i2cm.h"
+
+
+/**
+ * @ingroup i2cm
+ * @{
+ */
+    
+///@cond
+// No Doxygen documentation for the items between here and endcond. 
+/* **** Definitions **** */
+#ifndef MXC_I2CM_TX_TIMEOUT
+#define MXC_I2CM_TX_TIMEOUT     0x5000      /**< Master Transmit Timeout in number of repetitive attempts to receive an ACK/NACK or for a transmission to occur */
+#endif
+
+#ifndef MXC_I2CM_RX_TIMEOUT
+#define MXC_I2CM_RX_TIMEOUT     0x5000      /**< Master Receive Timeout in number of attempts to check FIFO for received data from a slave */
+#endif
+
+#define I2CM_READ_BIT           0x0001      /**< Bit location to specify a read for the I2C protocol */
+///@cond
+#define I2CM_FIFO_DEPTH_3Q      ((3 * MXC_I2CM_FIFO_DEPTH) / 4)
+#define I2CM_FIFO_DEPTH_2Q      (MXC_I2CM_FIFO_DEPTH / 2)
+
+//
+/* **** Globals **** */
+
+/* Clock divider lookup table */
+static const uint32_t clk_div_table[3][8] = {
+    /* I2CM_SPEED_100KHZ */
+    {
+        // 12000000
+        ((6 << MXC_F_I2CM_FS_CLK_DIV_FS_FILTER_CLK_DIV_POS) |
+            (17 << MXC_F_I2CM_FS_CLK_DIV_FS_SCL_HI_CNT_POS) |
+            (72 << MXC_F_I2CM_FS_CLK_DIV_FS_SCL_LO_CNT_POS)),
+        // 24000000
+        ((12 << MXC_F_I2CM_FS_CLK_DIV_FS_FILTER_CLK_DIV_POS) |
+            (38 << MXC_F_I2CM_FS_CLK_DIV_FS_SCL_HI_CNT_POS) |
+            (144 << MXC_F_I2CM_FS_CLK_DIV_FS_SCL_LO_CNT_POS)),
+        // 36000000 NOT SUPPORTED
+        0,
+        // 48000000
+        ((24 << MXC_F_I2CM_FS_CLK_DIV_FS_FILTER_CLK_DIV_POS) |
+            (80 << MXC_F_I2CM_FS_CLK_DIV_FS_SCL_HI_CNT_POS) |
+            (288 << MXC_F_I2CM_FS_CLK_DIV_FS_SCL_LO_CNT_POS)),
+        // 60000000 NOT SUPPORTED
+        0,
+        // 72000000 NOT SUPPORTED
+        0,
+        // 84000000 NOT SUPPORTED
+        0,
+        // 96000000
+        ((48 << MXC_F_I2CM_FS_CLK_DIV_FS_FILTER_CLK_DIV_POS) |
+            (164 << MXC_F_I2CM_FS_CLK_DIV_FS_SCL_HI_CNT_POS) |
+            (576 << MXC_F_I2CM_FS_CLK_DIV_FS_SCL_LO_CNT_POS)),
+    },
+    /* I2CM_SPEED_400KHZ */
+    {
+        // 12000000
+        ((2 << MXC_F_I2CM_FS_CLK_DIV_FS_FILTER_CLK_DIV_POS) |
+            (1 << MXC_F_I2CM_FS_CLK_DIV_FS_SCL_HI_CNT_POS) |
+            (18 << MXC_F_I2CM_FS_CLK_DIV_FS_SCL_LO_CNT_POS)),
+        // 24000000
+        ((3 << MXC_F_I2CM_FS_CLK_DIV_FS_FILTER_CLK_DIV_POS) |
+            (5 << MXC_F_I2CM_FS_CLK_DIV_FS_SCL_HI_CNT_POS) |
+            (36 << MXC_F_I2CM_FS_CLK_DIV_FS_SCL_LO_CNT_POS)),
+        // 36000000 NOT SUPPORTED
+        0,
+        // 48000000
+        ((6 << MXC_F_I2CM_FS_CLK_DIV_FS_FILTER_CLK_DIV_POS) |
+            (15 << MXC_F_I2CM_FS_CLK_DIV_FS_SCL_HI_CNT_POS) |
+            (72 << MXC_F_I2CM_FS_CLK_DIV_FS_SCL_LO_CNT_POS)),
+        // 60000000 NOT SUPPORTED
+        0,
+        // 72000000 NOT SUPPORTED
+        0,
+        // 84000000 NOT SUPPORTED
+        0,
+        // 96000000
+        ((12 << MXC_F_I2CM_FS_CLK_DIV_FS_FILTER_CLK_DIV_POS) |
+            (33 << MXC_F_I2CM_FS_CLK_DIV_FS_SCL_HI_CNT_POS) |
+            (144 << MXC_F_I2CM_FS_CLK_DIV_FS_SCL_LO_CNT_POS)),
+    },
+};
+
+// Saves the state of the non-blocking requests
+typedef enum {
+    I2CM_STATE_READING = 0,
+    I2CM_STATE_WRITING = 1
+} i2cm_state_t;
+
+typedef struct {
+    i2cm_req_t *req;
+    i2cm_state_t state;
+} i2cm_req_state_t;
+static i2cm_req_state_t states[MXC_CFG_I2CM_INSTANCES];
+
+/* **** Local Function Prototypes **** */
+
+static void I2CM_FreeCallback(int i2cm_num, int error);
+
+static int I2CM_Rx(mxc_i2cm_regs_t *i2cm, mxc_i2cm_fifo_regs_t *fifo, uint8_t addr,
+    uint8_t *data, uint32_t len);
+
+static int I2CM_CmdHandler(mxc_i2cm_regs_t *i2cm, mxc_i2cm_fifo_regs_t *fifo, i2cm_req_t *req);
+static int I2CM_ReadHandler(mxc_i2cm_regs_t *i2cm, i2cm_req_t *req, int i2cm_num);
+static int I2CM_WriteHandler(mxc_i2cm_regs_t *i2cm, i2cm_req_t *req, int i2cm_num);
+///@endcond 
+//
+/* ************************************************************************* */
+int I2CM_Init(mxc_i2cm_regs_t *i2cm, const sys_cfg_i2cm_t *sys_cfg, i2cm_speed_t speed)
+{
+    int err, clki;
+
+    // Check the base pointer
+    MXC_ASSERT(MXC_I2CM_GET_IDX(i2cm) >= 0);
+
+    // Set system level configurations
+    if ((err = SYS_I2CM_Init(i2cm, sys_cfg)) != E_NO_ERROR) {
+        return err;
+    }
+
+    // Compute clock array index
+    clki = ((SYS_I2CM_GetFreq(i2cm) / 12000000) - 1);
+
+    // Get clock divider settings from lookup table
+    if ((speed == I2CM_SPEED_100KHZ) && (clk_div_table[I2CM_SPEED_100KHZ][clki] > 0)) {
+        i2cm->fs_clk_div = clk_div_table[I2CM_SPEED_100KHZ][clki];
+
+    } else if ((speed == I2CM_SPEED_400KHZ) && (clk_div_table[I2CM_SPEED_400KHZ][clki] > 0)) {
+        i2cm->fs_clk_div = clk_div_table[I2CM_SPEED_400KHZ][clki];
+
+    } else {
+        // Requested speed is not achievable with the current clock setup
+        return E_NOT_SUPPORTED;
+    }
+
+    // Reset module
+    i2cm->ctrl = MXC_F_I2CM_CTRL_MSTR_RESET_EN;
+    i2cm->ctrl = 0;
+
+    // Set timeout to 255 ms and turn on the auto-stop option
+    i2cm->timeout = (MXC_F_I2CM_TIMEOUT_TX_TIMEOUT | MXC_F_I2CM_TIMEOUT_AUTO_STOP_EN);
+
+    // Enable tx_fifo and rx_fifo
+    i2cm->ctrl |= (MXC_F_I2CM_CTRL_TX_FIFO_EN | MXC_F_I2CM_CTRL_RX_FIFO_EN);
+
+    return E_NO_ERROR;
+}
+
+/* ************************************************************************* */
+int I2CM_Shutdown(mxc_i2cm_regs_t *i2cm)
+{
+    int i2cm_num, err;
+
+    // Check the base pointer
+    i2cm_num = MXC_I2CM_GET_IDX(i2cm);
+    MXC_ASSERT(i2cm_num >= 0);
+
+    // Disable and clear interrupts
+    i2cm->inten = 0;
+    i2cm->intfl = i2cm->intfl;
+
+    // Call all of the pending callbacks for this I2CM
+    if(states[i2cm_num].req != NULL) {
+        I2CM_Recover(i2cm);
+        I2CM_FreeCallback(i2cm_num, E_SHUTDOWN);
+    }
+
+    // Clears system level configurations
+    if ((err = SYS_I2CM_Shutdown(i2cm)) != E_NO_ERROR) {
+        return err;
+    }
+
+    return E_NO_ERROR;
+}
+
+
+/* ************************************************************************* */
+int I2CM_Read(mxc_i2cm_regs_t *i2cm, uint8_t addr, const uint8_t *cmd_data,
+    uint32_t cmd_len, uint8_t* data, uint32_t len)
+{
+    int i2cm_num;
+    int error = E_NO_ERROR;
+    int retval = E_NO_ERROR;
+    mxc_i2cm_fifo_regs_t *fifo;
+
+    if(data == NULL) {
+        return E_NULL_PTR;
+    }
+
+    // Make sure the I2CM has been initialized
+    if(i2cm->ctrl == 0) {
+        return E_UNINITIALIZED;
+    }
+
+    if(!(len > 0)) {
+        return E_NO_ERROR;
+    }
+
+    // Lock this I2CM
+    i2cm_num = MXC_I2CM_GET_IDX(i2cm);
+    while(mxc_get_lock((uint32_t*)&states[i2cm_num].req,1) != E_NO_ERROR) {}
+
+    // Get the FIFO pointer for this I2CM
+    fifo = MXC_I2CM_GET_FIFO(i2cm_num);
+
+    // Disable and clear the interrupts
+	i2cm->inten = 0;
+    i2cm->intfl = i2cm->intfl;
+
+    // Transmit the command if there is command data and length
+    if((cmd_data != NULL) && (cmd_len > 0)) {
+        retval = I2CM_Tx(i2cm, fifo, addr, cmd_data, cmd_len, 0);
+    }
+
+    // Read data from the slave if we don't have any errors
+    if(retval == E_NO_ERROR) {
+        retval = I2CM_Rx(i2cm, fifo, addr, data, len);
+    }
+
+    // Wait for the transaction to complete
+    if((error = I2CM_TxInProgress(i2cm)) != E_NO_ERROR) {
+        retval = error;
+    }
+
+    // Unlock this I2CM
+    mxc_free_lock((uint32_t*)&states[i2cm_num].req);
+
+    if(retval != E_NO_ERROR) {
+        return retval;
+    }
+
+    return len;
+}
+
+/* ************************************************************************* */
+int I2CM_Write(mxc_i2cm_regs_t *i2cm, uint8_t addr, const uint8_t *cmd_data,
+    uint32_t cmd_len, uint8_t* data, uint32_t len)
+{
+    int i2cm_num;
+    int error = E_NO_ERROR;
+    int retval = E_NO_ERROR;
+    mxc_i2cm_fifo_regs_t *fifo;
+
+    if(data == NULL) {
+        return E_NULL_PTR;
+    }
+
+    // Make sure the I2CM has been initialized
+    if(i2cm->ctrl == 0) {
+        return E_UNINITIALIZED;
+    }
+
+    if(!(len > 0)) {
+        return E_NO_ERROR;
+    }
+
+    // Lock this I2CM
+    i2cm_num = MXC_I2CM_GET_IDX(i2cm);
+    while(mxc_get_lock((uint32_t*)&states[i2cm_num].req,1) != E_NO_ERROR) {}
+
+    // Get the FIFO pointer for this I2CM
+    fifo = MXC_I2CM_GET_FIFO(i2cm_num);
+
+    // Disable and clear the interrupts
+	i2cm->inten = 0;
+    i2cm->intfl = i2cm->intfl;
+
+    // Transmit the command if there is command data and length, don't send stop bit
+    if((cmd_data != NULL) && (cmd_len > 0)) {
+        retval = I2CM_Tx(i2cm, fifo, addr, cmd_data, cmd_len, 0);
+    }
+
+    // Write data to the slave, send the stop bit
+    if(retval == E_NO_ERROR) {
+        retval = I2CM_Tx(i2cm, fifo, addr, data, len, 1);
+    }
+
+    // Wait for the transaction to complete
+    if((error = I2CM_TxInProgress(i2cm)) != E_NO_ERROR) {
+        retval = error;
+    }
+
+    // Unlock this I2CM
+    mxc_free_lock((uint32_t*)&states[i2cm_num].req);
+
+    if(retval != E_NO_ERROR) {
+        return retval;
+    }
+
+    return len;
+}
+
+/* ************************************************************************* */
+int I2CM_ReadAsync(mxc_i2cm_regs_t *i2cm, i2cm_req_t *req)
+{
+    int i2cm_num, error;
+
+    if(req->data == NULL) {
+        return E_NULL_PTR;
+    }
+
+    // Make sure the I2CM has been initialized
+    if(i2cm->ctrl == 0) {
+        return E_UNINITIALIZED;
+    }
+
+    if(!(req->data_len > 0)) {
+        return E_NO_ERROR;
+    }
+
+    i2cm_num = MXC_I2CM_GET_IDX(i2cm);
+
+    // Attempt to register this request
+    if(mxc_get_lock((uint32_t*)&states[i2cm_num].req, (uint32_t)req) != E_NO_ERROR) {
+        return E_BUSY;
+    }
+
+    states[i2cm_num].state = I2CM_STATE_READING;
+
+    // Clear the number of bytes counter
+    req->cmd_num = 0;
+    req->data_num = 0;
+
+    // Disable and clear the interrupts
+    i2cm->inten = 0;
+    i2cm->intfl = i2cm->intfl;
+
+    // Start the read
+    if((error = I2CM_ReadHandler(i2cm, req, i2cm_num)) != E_NO_ERROR) {
+        I2CM_Recover(i2cm);
+        I2CM_FreeCallback(i2cm_num, error);
+        return error;
+    }
+
+    return E_NO_ERROR;
+}
+
+/* ************************************************************************* */
+int I2CM_WriteAsync(mxc_i2cm_regs_t *i2cm, i2cm_req_t *req)
+{
+    int i2cm_num, error;
+
+    if(req->data == NULL) {
+        return E_NULL_PTR;
+    }
+
+    // Make sure the I2CM has been initialized
+    if(i2cm->ctrl == 0) {
+        return E_UNINITIALIZED;
+    }
+
+    if(!(req->data_len > 0)) {
+        return E_NO_ERROR;
+    }
+
+    i2cm_num = MXC_I2CM_GET_IDX(i2cm);
+
+    // Attempt to register this request
+    if(mxc_get_lock((uint32_t*)&states[i2cm_num].req, (uint32_t)req) != E_NO_ERROR) {
+        return E_BUSY;
+    }
+
+    states[i2cm_num].state = I2CM_STATE_WRITING;
+
+    // Clear the number of bytes counter
+    req->cmd_num = 0;
+    req->data_num = 0;
+
+    // Disable and clear the interrupts
+    i2cm->inten = 0;
+    i2cm->intfl = i2cm->intfl;
+
+    // Start the Write
+    if((error = I2CM_WriteHandler(i2cm, req, i2cm_num)) != E_NO_ERROR) {
+        I2CM_Recover(i2cm);
+        I2CM_FreeCallback(i2cm_num, error);
+        return error;
+    }
+
+    return E_NO_ERROR;
+}
+
+/* ************************************************************************* */
+int I2CM_AbortAsync(i2cm_req_t *req)
+{
+    int i2cm_num;
+    mxc_i2cm_regs_t *i2cm;
+
+    // Find the request, set to NULL
+    for(i2cm_num = 0; i2cm_num < MXC_CFG_I2CM_INSTANCES; i2cm_num++)
+    {
+        if(req == states[i2cm_num].req) {
+
+            i2cm = MXC_I2CM_GET_I2CM(i2cm_num);
+            I2CM_Recover(i2cm);
+            I2CM_FreeCallback(i2cm_num, E_ABORT);
+
+            return E_NO_ERROR;
+        }
+    }
+
+    return E_BAD_PARAM;
+}
+
+/* ************************************************************************* */
+void I2CM_Handler(mxc_i2cm_regs_t *i2cm)
+{
+    uint32_t intfl;
+    int i2cm_num, error;
+
+    // Save and clear the interrupts
+    intfl = i2cm->intfl;
+    i2cm->intfl = intfl;
+
+    // Mask the disabled interrupts
+    intfl &= i2cm->inten;
+
+    i2cm_num = MXC_I2CM_GET_IDX(i2cm);
+
+    // Check for errors
+    if ((intfl & MXC_F_I2CM_INTFL_TX_NACKED) || (intfl & MXC_F_I2CM_INTFL_TX_LOST_ARBITR)) {
+        I2CM_Recover(i2cm);
+        I2CM_FreeCallback(i2cm_num, E_COMM_ERR);
+        return;
+    }
+
+    if(intfl & MXC_F_I2CM_INTFL_TX_TIMEOUT) {
+        I2CM_Recover(i2cm);
+        I2CM_FreeCallback(i2cm_num, E_TIME_OUT);
+        return;
+    }
+
+    // Read or write
+    if(states[i2cm_num].state == I2CM_STATE_READING) {
+        if((error = I2CM_ReadHandler(i2cm, states[i2cm_num].req, i2cm_num)) != E_NO_ERROR) {
+            I2CM_Recover(i2cm);
+            I2CM_FreeCallback(i2cm_num, error);
+            return;
+        }
+
+    } else if(states[i2cm_num].state == I2CM_STATE_WRITING) {
+        if((error = I2CM_WriteHandler(i2cm, states[i2cm_num].req, i2cm_num)) != E_NO_ERROR) {
+            I2CM_Recover(i2cm);
+            I2CM_FreeCallback(i2cm_num, error);
+            return;
+        }
+    }
+
+    // Done with the transaction
+    if(intfl & MXC_F_I2CM_INTFL_TX_DONE) {
+        I2CM_Recover(i2cm);
+        I2CM_FreeCallback(i2cm_num, E_NO_ERROR);
+    }
+
+}
+
+/* ************************************************************************* */
+int I2CM_Busy(mxc_i2cm_regs_t *i2cm)
+{
+    // Check to see if there are any ongoing transactions
+    if((states[MXC_I2CM_GET_IDX(i2cm)].req == NULL) &&
+        !(i2cm->trans & MXC_F_I2CM_TRANS_TX_IN_PROGRESS)) {
+
+        return E_NO_ERROR;
+    }
+
+    return E_BUSY;
+}
+
+/* ************************************************************************* */
+int I2CM_PrepForSleep(mxc_i2cm_regs_t *i2cm)
+{
+    if(I2CM_Busy(i2cm) != E_NO_ERROR) {
+        return E_BUSY;
+    }
+
+    // Disable interrupts
+    i2cm->inten = 0;
+    return E_NO_ERROR;
+}
+
+/* ************************************************************************* */
+int I2CM_BusCheck(mxc_i2cm_regs_t *i2cm)
+{
+    // If SCL is low, we don't have the bus
+    if(!(i2cm->bb & MXC_F_I2CM_BB_BB_SCL_IN_VAL)) {
+        return E_BUSY;
+    }
+
+    // If SDA is low, we don't have the bus
+    if(!(i2cm->bb & MXC_F_I2CM_BB_BB_SDA_IN_VAL)) {
+        return E_BUSY;
+    }
+
+    return E_NO_ERROR;
+}
+
+/* ************************************************************************* */
+static void I2CM_FreeCallback(int i2cm_num, int error)
+{
+    // Save the request
+    i2cm_req_t *temp_req = states[i2cm_num].req;
+
+    // Unlock this UART to write
+    mxc_free_lock((uint32_t*)&states[i2cm_num].req);
+
+    // Callback if not NULL
+    if(temp_req->callback != NULL) {
+        temp_req->callback(temp_req, error);
+    }
+}
+
+/* ************************************************************************* */
+void I2CM_Recover(mxc_i2cm_regs_t *i2cm)
+{
+    // Disable and clear interrupts
+    i2cm->inten = 0;
+    i2cm->intfl = i2cm->intfl;
+    i2cm->ctrl = MXC_F_I2CM_CTRL_MSTR_RESET_EN;
+    i2cm->ctrl = MXC_F_I2CM_CTRL_TX_FIFO_EN | MXC_F_I2CM_CTRL_RX_FIFO_EN;
+}
+
+/* ************************************************************************* */
+int I2CM_WriteTxFifo(mxc_i2cm_regs_t *i2cm, mxc_i2cm_fifo_regs_t *fifo, const uint16_t data)
+{
+    int32_t timeout = MXC_I2CM_TX_TIMEOUT;
+
+    // Read the TX FIFO to determine if it's full
+    do {
+
+        // Wait for the TX FIFO to have room and check for errors
+        if (i2cm->intfl & (MXC_F_I2CM_INTFL_TX_NACKED |
+            MXC_F_I2CM_INTFL_TX_LOST_ARBITR)) {
+
+            return E_COMM_ERR;
+        }
+
+        if((i2cm->intfl & MXC_F_I2CM_INTFL_TX_TIMEOUT) || !timeout--) {
+            return E_TIME_OUT;
+        }
+
+    } while (fifo->tx);
+
+    fifo->tx = data;
+
+    return E_NO_ERROR;
+}
+
+/* ************************************************************************* */
+int I2CM_TxInProgress(mxc_i2cm_regs_t *i2cm)
+{
+    int32_t timeout = MXC_I2CM_TX_TIMEOUT;
+
+    while ((i2cm->trans & MXC_F_I2CM_TRANS_TX_IN_PROGRESS) && --timeout);
+
+    if (i2cm->intfl & (MXC_F_I2CM_INTFL_TX_NACKED |
+        MXC_F_I2CM_INTFL_TX_LOST_ARBITR)) {
+
+        I2CM_Recover(i2cm);
+        return E_COMM_ERR;
+    }
+
+    if((i2cm->intfl & MXC_F_I2CM_INTFL_TX_TIMEOUT) && !timeout--) {
+        I2CM_Recover(i2cm);
+        return E_TIME_OUT;
+    }
+
+    return E_NO_ERROR;
+}
+
+/* ************************************************************************* */
+int I2CM_Tx(mxc_i2cm_regs_t *i2cm, mxc_i2cm_fifo_regs_t *fifo, uint8_t addr,
+    const uint8_t *data, uint32_t len, uint8_t stop)
+{
+    uint32_t i;
+    int error;
+
+    // Write the address to the TXFIFO
+    if((error = I2CM_WriteTxFifo(i2cm, fifo, (MXC_S_I2CM_TRANS_TAG_START | (addr << 1)))) != E_NO_ERROR) {
+        return error;
+    }
+
+    // Start the transaction if it is not currently ongoing
+    if (!(i2cm->trans & MXC_F_I2CM_TRANS_TX_IN_PROGRESS)) {
+        i2cm->trans |= MXC_F_I2CM_TRANS_TX_START;
+    }
+
+    // Fill the FIFO
+    for (i = 0; i < len; i++) {
+        if((error = I2CM_WriteTxFifo(i2cm, fifo, (MXC_S_I2CM_TRANS_TAG_TXDATA_ACK | data[i]))) != E_NO_ERROR) {
+            return error;
+        }
+    }
+
+    // Send the stop condition
+    if(stop) {
+        if ((error = I2CM_WriteTxFifo(i2cm, fifo, MXC_S_I2CM_TRANS_TAG_STOP)) != E_NO_ERROR) {
+            return error;
+        }
+    }
+
+    return E_NO_ERROR;
+}
+
+/* ************************************************************************* */
+static int I2CM_Rx(mxc_i2cm_regs_t *i2cm, mxc_i2cm_fifo_regs_t *fifo, uint8_t addr,
+    uint8_t *data, uint32_t len)
+{
+    uint32_t i = len;
+    int32_t timeout;
+    uint16_t temp;
+    int error;
+
+    // Write the address to the TXFIFO
+    if((error = I2CM_WriteTxFifo(i2cm, fifo, (MXC_S_I2CM_TRANS_TAG_START |
+        (addr << 1) | I2CM_READ_BIT))) != E_NO_ERROR) {
+
+        return error;
+    }
+
+    // Write to the TXFIFO the number of bytes we want to read
+    while(i > 256) {
+        if((error = I2CM_WriteTxFifo(i2cm, fifo, (MXC_S_I2CM_TRANS_TAG_RXDATA_COUNT | 255))) != E_NO_ERROR) {
+            return error;
+        }
+
+        i -= 256;
+    }
+
+    if(i > 1) {
+        if((error = I2CM_WriteTxFifo(i2cm, fifo, (MXC_S_I2CM_TRANS_TAG_RXDATA_COUNT | (i-2)))) != E_NO_ERROR) {
+            return error;
+        }
+    }
+
+    // Start the transaction if it is not currently ongoing
+    if (!(i2cm->trans & MXC_F_I2CM_TRANS_TX_IN_PROGRESS)) {
+        i2cm->trans |= MXC_F_I2CM_TRANS_TX_START;
+    }
+
+
+    // NACK the last read byte
+    if((error = I2CM_WriteTxFifo(i2cm, fifo, (MXC_S_I2CM_TRANS_TAG_RXDATA_NACK))) != E_NO_ERROR) {
+        return error;
+    }
+
+    // Send the stop condition
+    if ((error = I2CM_WriteTxFifo(i2cm, fifo, MXC_S_I2CM_TRANS_TAG_STOP)) != E_NO_ERROR) {
+        return error;
+    }
+
+    // Get the data from the RX FIFO
+    i = 0;
+    while (i < len) {
+
+        // Wait for there to be data in the RX FIFO
+        timeout = MXC_I2CM_RX_TIMEOUT;
+        while (!(i2cm->intfl & MXC_F_I2CM_INTFL_RX_FIFO_NOT_EMPTY) &&
+            ((i2cm->bb & MXC_F_I2CM_BB_RX_FIFO_CNT) == 0)) {
+
+            if((timeout-- < 0) || (i2cm->trans & MXC_F_I2CM_TRANS_TX_TIMEOUT)) {
+                return E_TIME_OUT;
+            }
+
+            if (i2cm->trans & (MXC_F_I2CM_TRANS_TX_LOST_ARBITR | MXC_F_I2CM_TRANS_TX_NACKED)) {
+                return E_COMM_ERR;
+            }
+        }
+        i2cm->intfl = MXC_F_I2CM_INTFL_RX_FIFO_NOT_EMPTY;
+
+        // Save the data from the RX FIFO
+        temp = fifo->rx;
+        if (temp & MXC_S_I2CM_RSTLS_TAG_EMPTY) {
+            continue;
+        }
+        data[i++] = (uint8_t)temp;
+    }
+
+    return E_NO_ERROR;
+}
+
+/* ************************************************************************* */
+static int I2CM_CmdHandler(mxc_i2cm_regs_t *i2cm, mxc_i2cm_fifo_regs_t *fifo, i2cm_req_t *req)
+{
+    int error;
+
+    // Start of the command
+    if(req->cmd_num == 0) {
+
+        // Write the address to the TXFIFO
+        if((error = I2CM_WriteTxFifo(i2cm, fifo, (MXC_S_I2CM_TRANS_TAG_START | (req->addr << 1)))) != E_NO_ERROR) {
+            return error;
+        }
+
+        // Start the transaction if it is not currently ongoing
+        if (!(i2cm->trans & MXC_F_I2CM_TRANS_TX_IN_PROGRESS)) {
+            i2cm->trans |= MXC_F_I2CM_TRANS_TX_START;
+        }
+    }
+
+    // Write to the FIFO until it is full or we run out of command bytes
+    while((req->cmd_num < req->cmd_len) && (!fifo->tx)) {
+        fifo->tx = MXC_S_I2CM_TRANS_TAG_TXDATA_ACK | req->cmd_data[req->cmd_num++];
+    }
+
+    return E_NO_ERROR;
+}
+
+/* ************************************************************************* */
+static int I2CM_ReadHandler(mxc_i2cm_regs_t *i2cm, i2cm_req_t *req, int i2cm_num)
+{
+    int error, cmd_remain, data_remain;
+    uint16_t data;
+    uint32_t temp_len, inten;
+    mxc_i2cm_fifo_regs_t *fifo;
+
+    // Get the FIFO pointer for this I2CM
+    fifo = MXC_I2CM_GET_FIFO(i2cm_num);
+
+    cmd_remain = req->cmd_len - req->cmd_num;
+    data_remain = req->data_len - req->data_num;
+
+    // Process the command portion
+    if((cmd_remain) && (req->cmd_data != NULL)) {
+        if((error = I2CM_CmdHandler(i2cm, fifo, req)) != E_NO_ERROR) {
+            return error;
+        }
+
+        cmd_remain = req->cmd_len - req->cmd_num;
+    }
+
+    // Process the data portion
+    if((cmd_remain == 0) && (data_remain)) {
+
+        // Save the data from the RXFIFO
+        data = fifo->rx;
+        while((req->data_num < req->data_len) && !(data & MXC_S_I2CM_RSTLS_TAG_EMPTY)) {
+            req->data[req->data_num++] = data;
+            data = fifo->rx;
+        }
+
+        // Start of the data portion
+        if(req->data_num == 0) {
+
+            temp_len = req->data_len;
+
+            // Write the address to the TXFIFO
+            if((error = I2CM_WriteTxFifo(i2cm, fifo, (MXC_S_I2CM_TRANS_TAG_START |
+                (req->addr << 1) | I2CM_READ_BIT))) != E_NO_ERROR) {
+
+                return error;
+            }
+
+            // Write to the TXFIFO the number of bytes we want to read
+            while(temp_len > 256) {
+                if((error = I2CM_WriteTxFifo(i2cm, fifo, (MXC_S_I2CM_TRANS_TAG_RXDATA_COUNT | 255))) != E_NO_ERROR) {
+                    return error;
+                }
+
+                temp_len -= 256;
+            }
+
+            if(temp_len > 1) {
+                if((error = I2CM_WriteTxFifo(i2cm, fifo, (MXC_S_I2CM_TRANS_TAG_RXDATA_COUNT | (temp_len-2)))) != E_NO_ERROR) {
+                    return error;
+                }
+            }
+
+            // Start the transaction if it is not currently ongoing
+            if (!(i2cm->trans & MXC_F_I2CM_TRANS_TX_IN_PROGRESS)) {
+                i2cm->trans |= MXC_F_I2CM_TRANS_TX_START;
+            }
+
+            // NACK the last read byte
+            if((error = I2CM_WriteTxFifo(i2cm, fifo, (MXC_S_I2CM_TRANS_TAG_RXDATA_NACK))) != E_NO_ERROR) {
+                return error;
+            }
+
+            // Send the stop condition
+            if ((error = I2CM_WriteTxFifo(i2cm, fifo, MXC_S_I2CM_TRANS_TAG_STOP)) != E_NO_ERROR) {
+                return error;
+            }
+        }
+    }
+
+    // Enable the required interrupts
+    inten = MXC_F_I2CM_INTEN_TX_DONE | MXC_F_I2CM_INTEN_TX_NACKED |
+        MXC_F_I2CM_INTEN_TX_LOST_ARBITR | MXC_F_I2CM_INTEN_TX_TIMEOUT;
+
+    if (cmd_remain) {
+        inten |= (MXC_F_I2CM_INTEN_TX_FIFO_EMPTY | MXC_F_I2CM_INTEN_TX_FIFO_3Q_EMPTY);
+    }
+
+    data_remain = req->data_len - req->data_num;
+    if (data_remain > I2CM_FIFO_DEPTH_3Q) {
+        inten |= MXC_F_I2CM_INTEN_RX_FIFO_3Q_FULL;
+
+    } else if (data_remain > I2CM_FIFO_DEPTH_2Q) {
+        inten |= MXC_F_I2CM_INTEN_RX_FIFO_2Q_FULL;
+
+    } else if (data_remain > 0) {
+        inten |= MXC_F_I2CM_INTEN_RX_FIFO_NOT_EMPTY;
+    }
+
+    i2cm->inten = inten;
+
+    return E_NO_ERROR;
+}
+
+/* ************************************************************************* */
+static int I2CM_WriteHandler(mxc_i2cm_regs_t *i2cm, i2cm_req_t *req, int i2cm_num)
+{
+    int error, cmd_remain, data_remain;
+    uint32_t inten;
+    mxc_i2cm_fifo_regs_t *fifo;
+
+    // Get the FIFO pointer for this I2CM
+    fifo = MXC_I2CM_GET_FIFO(i2cm_num);
+
+    cmd_remain = req->cmd_len - req->cmd_num;
+    data_remain = req->data_len - req->data_num;
+
+    // Process the command portion
+    if((cmd_remain) && (req->cmd_data != NULL)) {
+        if((error = I2CM_CmdHandler(i2cm, fifo, req)) != E_NO_ERROR) {
+            return error;
+        }
+
+        cmd_remain = req->cmd_len - req->cmd_num;
+    }
+
+    // Process the data portion
+    if((cmd_remain == 0) && (data_remain)) {
+
+        // Start of the data portion
+        if(req->data_num == 0) {
+
+            // Write the address to the TXFIFO
+            if((error = I2CM_WriteTxFifo(i2cm, fifo, (MXC_S_I2CM_TRANS_TAG_START |
+                (req->addr << 1)))) != E_NO_ERROR) {
+
+                return error;
+            }
+
+            // Start the transaction if it is not currently ongoing
+            if (!(i2cm->trans & MXC_F_I2CM_TRANS_TX_IN_PROGRESS)) {
+                i2cm->trans |= MXC_F_I2CM_TRANS_TX_START;
+            }
+        }
+
+        // Write bytes to the FIFO until it's full or we run out of bytes
+        while(req->data_num < req->data_len) {
+            fifo->tx = MXC_S_I2CM_TRANS_TAG_TXDATA_ACK | req->data[req->data_num++];
+        }
+
+        // Send the stop condition
+        if ((error = I2CM_WriteTxFifo(i2cm, fifo, MXC_S_I2CM_TRANS_TAG_STOP)) != E_NO_ERROR) {
+            return error;
+        }
+    }
+
+    // Enable the required interrupts
+    data_remain = req->data_len - req->data_num;
+    inten = MXC_F_I2CM_INTEN_TX_DONE | MXC_F_I2CM_INTEN_TX_NACKED |
+        MXC_F_I2CM_INTEN_TX_LOST_ARBITR | MXC_F_I2CM_INTEN_TX_TIMEOUT;
+
+    if(data_remain || cmd_remain) {
+        inten |= (MXC_F_I2CM_INTEN_TX_FIFO_EMPTY | MXC_F_I2CM_INTEN_TX_FIFO_3Q_EMPTY);
+    }
+    i2cm->inten = inten;
+
+    return E_NO_ERROR;
+}
+/**@} end of group i2cm */