Newer
Older
mbed-os / targets / TARGET_Maxim / TARGET_MAX32670 / Libraries / PeriphDrivers / Source / I2C / i2c_reva.c
/******************************************************************************
 * Copyright (C) 2023 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.
 *
 ******************************************************************************/

#include <stdio.h>
#include <stddef.h>
#include <stdint.h>
#include "mxc_device.h"
#include "mxc_assert.h"
#include "mxc_lock.h"
#include "mxc_sys.h"
#include "mxc_delay.h"
#include "i2c_regs.h"
#include "mxc_i2c.h"
#include "i2c_reva.h"
#include "dma.h"

/* **** Variable Declaration **** */
typedef struct {
    mxc_i2c_reva_req_t *req;
    int master; // 1 for Master, 0 for slave
    int channelTx; // DMA channel for TX transaction
    int channelRx; // DMA channel for RX transaction
    volatile int writeDone; // Write done flag
    volatile int readDone; // Flag done flag
} mxc_i2c_reva_req_state_t;

static mxc_i2c_reva_req_state_t states[MXC_I2C_INSTANCES];

void *AsyncRequests[MXC_I2C_INSTANCES];
unsigned int AsyncWritten[MXC_I2C_INSTANCES];
unsigned int AsyncRead[MXC_I2C_INSTANCES];

/* **** Function Prototypes **** */
void MXC_I2C_RevA_AsyncCallback(mxc_i2c_reva_regs_t *i2c, int retVal);
void MXC_I2C_RevA_AsyncStop(mxc_i2c_reva_regs_t *i2c);
void MXC_I2C_RevA_AbortAsync(mxc_i2c_reva_regs_t *i2c);
void MXC_I2C_RevA_MasterAsyncHandler(int i2cNum);
int MXC_I2C_RevA_DMAHandler(mxc_i2c_reva_req_t *req);
void MXC_I2C_RevA_SlaveAsyncHandler(mxc_i2c_reva_regs_t *i2c, mxc_i2c_reva_slave_handler_t callback,
                                    uint32_t *int_en, int *retVal);

/* ************************************************************************* */
/* Control/Configuration functions                                           */
/* ************************************************************************* */
int MXC_I2C_RevA_Init(mxc_i2c_reva_regs_t *i2c, int masterMode, unsigned int slaveAddr)
{
    int err;

    if (i2c == NULL) {
        return E_NULL_PTR;
    }

    if ((err = MXC_I2C_Recover((mxc_i2c_regs_t *)i2c, 16)) != E_NO_ERROR) {
        return err;
    }

    i2c->ctrl |= MXC_F_I2C_REVA_CTRL_EN;

    MXC_I2C_ClearRXFIFO((mxc_i2c_regs_t *)i2c);
    MXC_I2C_ClearTXFIFO((mxc_i2c_regs_t *)i2c);
    // Set the thresholds here and allow the user to change them as needed
    MXC_I2C_SetTXThreshold((mxc_i2c_regs_t *)i2c, 2); // set TX threshold to 2 bytes
    MXC_I2C_SetRXThreshold((mxc_i2c_regs_t *)i2c, 6); // set RX threshold to 6 bytes

    if (!masterMode) {
        MXC_I2C_SetSlaveAddr((mxc_i2c_regs_t *)i2c, slaveAddr, 0);
        states[MXC_I2C_GET_IDX((mxc_i2c_regs_t *)i2c)].master = 0;
    } else {
        i2c->ctrl |= MXC_F_I2C_REVA_CTRL_MST_MODE;
        states[MXC_I2C_GET_IDX((mxc_i2c_regs_t *)i2c)].master = 1;
    }

    return E_NO_ERROR;
}

int MXC_I2C_RevA_SetSlaveAddr(mxc_i2c_reva_regs_t *i2c, unsigned int slaveAddr, int idx)
{
    if (i2c == NULL) {
        return E_NULL_PTR;
    }

    if (idx != 0) {
        // Multiple slaves are not supported yet
        return E_NOT_SUPPORTED;
    }

    if (slaveAddr > MXC_F_I2C_REVA_SLAVE_ADDR) {
        // Only support addresses up to 10 bits
        return E_BAD_PARAM;
    }

    i2c->slave = 0;

    if (slaveAddr > MXC_I2C_REVA_MAX_ADDR_WIDTH) {
        // Set for 10bit addressing mode
        i2c->slave = MXC_F_I2C_REVA_SLAVE_EXT_ADDR_EN;
    }

    i2c->slave |= slaveAddr;

    return E_NO_ERROR;
}

int MXC_I2C_RevA_Shutdown(mxc_i2c_reva_regs_t *i2c)
{
    return E_NOT_SUPPORTED;
}

int MXC_I2C_RevA_SetFrequency(mxc_i2c_reva_regs_t *i2c, unsigned int hz)
{
    unsigned int ticksTotal, hiClks, lowClks;

    if (i2c == NULL) {
        return E_NULL_PTR;
    }

    if (hz > MXC_I2C_REVA_FASTPLUS_SPEED) {
        // We're going to enable high speed
        int hsLowClks, hsHiClks;

        // Calculate the period of SCL and set up 33% duty cycle
        ticksTotal = PeripheralClock / hz;
        hsLowClks = (ticksTotal * 2) / 3 - 1;
        hsHiClks = ticksTotal / 3 - 1;

        // For rounding errors, adjust by 1 clock tick
        if (ticksTotal % 1) {
            hsHiClks++;
        }

        // If we're too slow for high speed, bail out
        if ((hsHiClks > 0xF) || (hsLowClks > 0xF)) {
            return E_BAD_PARAM;
        }

        hz = MXC_I2C_REVA_FAST_SPEED; // High speed preambles will be sent at 400kHz
    }

    // Calculate the period of SCL, 50% duty cycle
    ticksTotal = PeripheralClock / hz;
    hiClks = (ticksTotal >> 1) - 1;
    lowClks = (ticksTotal >> 1) - 1;

    // Adjust for rounding errors
    if (ticksTotal % 1) {
        hiClks++;
    }

    // Check for maximum/minimum supported speeds
    if ((hiClks > MXC_F_I2C_REVA_CLKHI_HI) || (lowClks == 0)) {
        return E_BAD_PARAM;
    }

    i2c->clklo = lowClks & MXC_F_I2C_REVA_CLKLO_LO;
    i2c->clkhi = hiClks & MXC_F_I2C_REVA_CLKHI_HI;

    // Return the actual speed set, since it won't be exactly what's requested
    return MXC_I2C_GetFrequency((mxc_i2c_regs_t *)i2c);
}

unsigned int MXC_I2C_RevA_GetFrequency(mxc_i2c_reva_regs_t *i2c)
{
    unsigned int sclCycles = 0;

    // Calculate the speed based on what we've written into registers
    sclCycles = (i2c->clklo & MXC_F_I2C_REVA_CLKLO_LO) + (i2c->clkhi & MXC_F_I2C_REVA_CLKHI_HI);

    return PeripheralClock / sclCycles;
}

int MXC_I2C_RevA_ReadyForSleep(mxc_i2c_reva_regs_t *i2c)
{
    if (MXC_I2C_GET_IDX((mxc_i2c_regs_t *)i2c) < 0) {
        return E_BAD_PARAM;
    }

    if (AsyncRequests[MXC_I2C_GET_IDX((mxc_i2c_regs_t *)i2c)] != NULL) {
        return E_BUSY;
    }

    return E_NO_ERROR;
}

int MXC_I2C_RevA_SetClockStretching(mxc_i2c_reva_regs_t *i2c, int enable)
{
    if (i2c == NULL) {
        return E_NULL_PTR;
    }

    if (enable) {
        i2c->ctrl &= ~MXC_F_I2C_REVA_CTRL_CLKSTR_DIS;
    } else {
        i2c->ctrl |= MXC_F_I2C_REVA_CTRL_CLKSTR_DIS;
    }

    return E_NO_ERROR;
}

int MXC_I2C_RevA_GetClockStretching(mxc_i2c_reva_regs_t *i2c)
{
    if (i2c == NULL) {
        return E_NULL_PTR;
    }

    return !((i2c->ctrl & MXC_F_I2C_REVA_CTRL_CLKSTR_DIS) >> MXC_F_I2C_REVA_CTRL_CLKSTR_DIS_POS);
}

/* ************************************************************************* */
/* Low-level functions                                                       */
/* ************************************************************************* */
int MXC_I2C_RevA_Start(mxc_i2c_reva_regs_t *i2c)
{
    if (i2c == NULL) {
        return E_NULL_PTR;
    }

    // If we have an incomplete transfer, we need to do a restart
    if (i2c->mstctrl & MXC_F_I2C_REVA_MSTCTRL_START) {
        i2c->mstctrl |= MXC_F_I2C_REVA_MSTCTRL_RESTART;
    } else {
        i2c->mstctrl |= MXC_F_I2C_REVA_MSTCTRL_START; // No check for start generation
    }

    return E_NO_ERROR;
}

int MXC_I2C_RevA_Stop(mxc_i2c_reva_regs_t *i2c)
{
    if (i2c == NULL) {
        return E_NULL_PTR;
    }

    i2c->mstctrl |= MXC_F_I2C_REVA_MSTCTRL_STOP;

    while (i2c->mstctrl & MXC_F_I2C_REVA_MSTCTRL_STOP) {}

    return E_NO_ERROR;
}

int MXC_I2C_RevA_WriteByte(mxc_i2c_reva_regs_t *i2c, unsigned char byte)
{
    if (i2c == NULL) {
        return E_NULL_PTR;
    }

    if (!(i2c->status & MXC_F_I2C_REVA_STATUS_TX_EM)) {
        return E_OVERFLOW;
    }

    // I'm depending on an interrupt flag here
    // This might cause issues with the transaction level functions to come
    MXC_I2C_ClearFlags((mxc_i2c_regs_t *)i2c, MXC_I2C_REVA_INTFL0_MASK, MXC_I2C_REVA_INTFL1_MASK);
    i2c->fifo = byte;

    while (!(i2c->status & MXC_F_I2C_REVA_STATUS_TX_EM)) {}

    return i2c->intfl0 & MXC_F_I2C_REVA_INTFL0_DATA_ERR;
}

int MXC_I2C_RevA_ReadByte(mxc_i2c_reva_regs_t *i2c, unsigned char *byte, int ack)
{
    if ((i2c == NULL) || (byte == NULL)) {
        return E_NULL_PTR;
    }

    if (i2c->status & MXC_F_I2C_REVA_STATUS_RX_EM) {
        return E_UNDERFLOW;
    }

    *byte = (uint8_t)(i2c->fifo & MXC_F_I2C_REVA_FIFO_DATA);

    if (ack) {
        i2c->ctrl &= ~MXC_F_I2C_REVA_CTRL_IRXM_ACK;
    } else {
        i2c->ctrl |= MXC_F_I2C_REVA_CTRL_IRXM_ACK;
    }

    return E_NO_ERROR;
}

int MXC_I2C_RevA_ReadByteInteractive(mxc_i2c_reva_regs_t *i2c, unsigned char *byte,
                                     mxc_i2c_reva_getAck_t getAck)
{
    if ((i2c == NULL) || (byte == NULL)) {
        return E_NULL_PTR;
    }

    if (!(i2c->status & MXC_F_I2C_REVA_STATUS_RX_EM)) {
        return E_UNDERFLOW;
    }

    *byte = (uint8_t)(i2c->fifo & MXC_F_I2C_REVA_FIFO_DATA);

    if (getAck == NULL) {
        i2c->ctrl &= ~MXC_F_I2C_REVA_CTRL_IRXM_ACK_POS;
    } else {
        i2c->ctrl |= (!!getAck((mxc_i2c_reva_regs_t *)i2c, *byte))
                     << MXC_F_I2C_REVA_CTRL_IRXM_ACK_POS;
    }

    return E_NO_ERROR;
}

int MXC_I2C_RevA_Write(mxc_i2c_reva_regs_t *i2c, unsigned char *bytes, unsigned int *len)
{
    int notAcked = 0;
    unsigned written = 0;

    if (i2c == NULL) {
        return E_NULL_PTR;
    }

    if ((bytes == NULL) || (len == NULL)) {
        return E_NULL_PTR;
    }

    for (; written < *len; written++) {
        int retVal = MXC_I2C_WriteByte((mxc_i2c_regs_t *)i2c, bytes[written]);

        if (retVal >= 0) {
            notAcked += retVal;
        } else {
            *len = written;
            return retVal;
        }
    }

    *len = written;
    notAcked = (notAcked > 0) ? 1 : 0;
    return notAcked;
}

int MXC_I2C_RevA_Read(mxc_i2c_reva_regs_t *i2c, unsigned char *bytes, unsigned int *len, int ack)
{
    unsigned read = 0;

    if (i2c == NULL) {
        return E_NULL_PTR;
    }

    if ((bytes == NULL) || (len == NULL)) {
        return E_NULL_PTR;
    }

    for (; read < *len - 1; read++) {
        int retVal = MXC_I2C_ReadByte((mxc_i2c_regs_t *)i2c, &(bytes[read]), 1);

        if (retVal != E_NO_ERROR) {
            *len = read;
            return retVal;
        }
    }

    read++;
    *len = read;
    return MXC_I2C_ReadByte((mxc_i2c_regs_t *)i2c, &(bytes[read]), ack);
}

int MXC_I2C_RevA_ReadRXFIFO(mxc_i2c_reva_regs_t *i2c, volatile unsigned char *bytes,
                            unsigned int len)
{
    unsigned read = 0;

    if ((i2c == NULL) || (bytes == NULL)) {
        return E_NULL_PTR;
    }

    while ((len > read) && (!(i2c->status & MXC_F_I2C_REVA_STATUS_RX_EM))) {
        bytes[read++] = i2c->fifo;
    }

    return read;
}

int MXC_I2C_RevA_ReadRXFIFODMA(mxc_i2c_reva_regs_t *i2c, unsigned char *bytes, unsigned int len,
                               mxc_i2c_reva_dma_complete_cb_t callback, mxc_dma_config_t config,
                               mxc_dma_regs_t *dma)
{
    uint8_t i2cNum;
    uint8_t channel;
    mxc_dma_srcdst_t srcdst;

    if ((i2c == NULL) || (bytes == NULL)) {
        return E_NULL_PTR;
    }

    i2cNum = MXC_I2C_GET_IDX((mxc_i2c_regs_t *)i2c);

#if TARGET_NUM == 32665
    channel = MXC_DMA_AcquireChannel(dma);
#else
    channel = MXC_DMA_AcquireChannel();
#endif

    config.ch = channel;

    config.srcwd = MXC_DMA_WIDTH_BYTE;
    config.dstwd = MXC_DMA_WIDTH_BYTE;

    config.srcinc_en = 0;
    config.dstinc_en = 1;

    srcdst.ch = channel;
    srcdst.dest = bytes;
    srcdst.len = len;

    states[i2cNum].channelRx = channel;
    MXC_DMA_ConfigChannel(config, srcdst);

    if (states[i2cNum].master) {
        MXC_DMA_SetCallback(channel, MXC_I2C_RevA_DMACallback);
    } else {
        MXC_DMA_SetCallback(channel, NULL);
    }

    MXC_DMA_EnableInt(channel);
    MXC_DMA_Start(channel);
    //MXC_DMA->ch[channel].cfg |= MXC_F_DMA_CFG_CTZIEN;
    MXC_DMA_SetChannelInterruptEn(channel, 0, 1);
    i2c->dma |= MXC_F_I2C_REVA_DMA_RX_EN;

    return E_NO_ERROR;
}

int MXC_I2C_RevA_GetRXFIFOAvailable(mxc_i2c_reva_regs_t *i2c)
{
    if (i2c == NULL) {
        return E_NULL_PTR;
    }

    return (i2c->rxctrl1 & MXC_F_I2C_REVA_RXCTRL1_LVL) >> MXC_F_I2C_REVA_RXCTRL1_LVL_POS;
}

int MXC_I2C_RevA_WriteTXFIFO(mxc_i2c_reva_regs_t *i2c, volatile unsigned char *bytes,
                             unsigned int len)
{
    unsigned written = 0;

    if ((i2c == NULL) || (bytes == NULL)) {
        return E_NULL_PTR;
    }

    while ((len > written) && (!(i2c->status & MXC_F_I2C_REVA_STATUS_TX_FULL))) {
        i2c->fifo = bytes[written++];
    }

    return written;
}

int MXC_I2C_RevA_WriteTXFIFODMA(mxc_i2c_reva_regs_t *i2c, unsigned char *bytes, unsigned int len,
                                mxc_i2c_reva_dma_complete_cb_t callback, mxc_dma_config_t config,
                                mxc_dma_regs_t *dma)
{
    uint8_t i2cNum;
    uint8_t channel;
    mxc_dma_srcdst_t srcdst;

    if ((i2c == NULL) || (bytes == NULL)) {
        return E_NULL_PTR;
    }

    i2cNum = MXC_I2C_GET_IDX((mxc_i2c_regs_t *)i2c);

    i2c->mstctrl |= MXC_F_I2C_REVA_MSTCTRL_START;

#if TARGET_NUM == 32665
    channel = MXC_DMA_AcquireChannel(dma);
#else
    channel = MXC_DMA_AcquireChannel();
#endif

    config.ch = channel;

    config.srcwd = MXC_DMA_WIDTH_BYTE;
    config.dstwd = MXC_DMA_WIDTH_BYTE;

    config.srcinc_en = 1;
    config.dstinc_en = 0;

    srcdst.ch = channel;
    srcdst.source = bytes;
    srcdst.len = len;

    states[i2cNum].channelTx = channel;
    MXC_DMA_ConfigChannel(config, srcdst);

    if (states[i2cNum].master) {
        MXC_DMA_SetCallback(channel, MXC_I2C_RevA_DMACallback);
    } else {
        MXC_DMA_SetCallback(channel, NULL);
    }

    MXC_DMA_EnableInt(channel);
    MXC_DMA_Start(channel);
    // MXC_DMA->ch[channel].cfg |= MXC_F_DMA_CFG_CTZIEN;
    MXC_DMA_SetChannelInterruptEn(channel, 0, 1);
    i2c->dma |= MXC_F_I2C_REVA_DMA_TX_EN;

    return E_NO_ERROR;
}

int MXC_I2C_RevA_GetTXFIFOAvailable(mxc_i2c_reva_regs_t *i2c)
{
    if (i2c == NULL) {
        return E_NULL_PTR;
    }

    int txFIFOlen = (i2c->fifolen & MXC_F_I2C_REVA_FIFOLEN_TX_DEPTH) >>
                    MXC_F_I2C_REVA_FIFOLEN_TX_DEPTH_POS;
    return txFIFOlen -
           ((i2c->txctrl1 & MXC_F_I2C_REVA_TXCTRL1_LVL) >> MXC_F_I2C_REVA_TXCTRL1_LVL_POS);
}

void MXC_I2C_RevA_ClearRXFIFO(mxc_i2c_reva_regs_t *i2c)
{
    i2c->rxctrl0 |= MXC_F_I2C_REVA_RXCTRL0_FLUSH;

    while (i2c->rxctrl0 & MXC_F_I2C_REVA_RXCTRL0_FLUSH) {}
}

void MXC_I2C_RevA_ClearTXFIFO(mxc_i2c_reva_regs_t *i2c)
{
    i2c->txctrl0 |= MXC_F_I2C_REVA_TXCTRL0_FLUSH;

    while (i2c->txctrl0 & MXC_F_I2C_REVA_TXCTRL0_FLUSH) {}
}

int MXC_I2C_RevA_GetFlags(mxc_i2c_reva_regs_t *i2c, unsigned int *flags0, unsigned int *flags1)
{
    if (i2c == NULL) {
        return E_NULL_PTR;
    }

    if ((flags0 == NULL) || (flags1 == NULL)) {
        return E_BAD_PARAM;
    }

    *flags0 = i2c->intfl0;
    *flags1 = i2c->intfl1;

    return E_NO_ERROR;
}

void MXC_I2C_RevA_ClearFlags(mxc_i2c_reva_regs_t *i2c, unsigned int flags0, unsigned int flags1)
{
    i2c->intfl0 = flags0;
    i2c->intfl1 = flags1;
}

void MXC_I2C_RevA_EnableInt(mxc_i2c_reva_regs_t *i2c, unsigned int flags0, unsigned int flags1)
{
    i2c->inten0 |= flags0;
    i2c->inten1 |= flags1;
}

void MXC_I2C_RevA_DisableInt(mxc_i2c_reva_regs_t *i2c, unsigned int flags0, unsigned int flags1)
{
    i2c->inten0 &= ~flags0;
    i2c->inten1 &= ~flags1;
}

int MXC_I2C_RevA_Recover(mxc_i2c_reva_regs_t *i2c, unsigned int retries)
{
    int err;
    unsigned int i;

    if (i2c == NULL) {
        return E_NULL_PTR;
    }

    err = E_COMM_ERR;

    i2c->ctrl |= MXC_F_I2C_REVA_CTRL_EN;
    int swBit = i2c->ctrl & MXC_F_I2C_REVA_CTRL_BB_MODE;

    if (i2c == NULL) {
        return E_NULL_PTR;
    }

    i2c->ctrl |= MXC_F_I2C_REVA_CTRL_BB_MODE;

    // Follow the procedure detailed in the header file
    // Delay 10uS between each step to give the line/slaves time to react
    for (i = 0; i < retries; i++) {
        MXC_Delay(10);
        i2c->ctrl &= ~MXC_F_I2C_REVA_CTRL_SCL_OUT;

        MXC_Delay(10);

        if (i2c->ctrl & MXC_F_I2C_REVA_CTRL_SCL) {
            i2c->ctrl |= MXC_F_I2C_REVA_CTRL_SCL_OUT | MXC_F_I2C_REVA_CTRL_SDA_OUT;
            continue; // Give up and try again
        }

        MXC_Delay(10);
        i2c->ctrl &= ~MXC_F_I2C_REVA_CTRL_SDA_OUT;

        MXC_Delay(10);

        if (i2c->ctrl & MXC_F_I2C_REVA_CTRL_SDA) {
            i2c->ctrl |= MXC_F_I2C_REVA_CTRL_SCL_OUT | MXC_F_I2C_REVA_CTRL_SDA_OUT;
            continue; // Give up and try again
        }

        MXC_Delay(10);
        i2c->ctrl |= MXC_F_I2C_REVA_CTRL_SDA_OUT;

        MXC_Delay(10);

        if (!(i2c->ctrl & MXC_F_I2C_REVA_CTRL_SDA)) {
            i2c->ctrl |= MXC_F_I2C_REVA_CTRL_SCL_OUT | MXC_F_I2C_REVA_CTRL_SDA_OUT;
            continue; // Give up and try again
        }

        MXC_Delay(10);
        i2c->ctrl |= MXC_F_I2C_REVA_CTRL_SCL_OUT;

        MXC_Delay(10);

        if (i2c->ctrl & MXC_F_I2C_REVA_CTRL_SCL) {
            err = E_NO_ERROR; // We have control
            break;
        }
    }

    if (swBit == 0) {
        i2c->ctrl &= ~MXC_F_I2C_REVA_CTRL_BB_MODE;
    }

    i2c->ctrl &= ~MXC_F_I2C_REVA_CTRL_EN;

    return err;
}

void MXC_I2C_RevA_EnablePreload(mxc_i2c_reva_regs_t *i2c)
{
    i2c->txctrl0 |= MXC_F_I2C_REVA_TXCTRL0_PRELOAD_MODE;
}

void MXC_I2C_RevA_DisablePreload(mxc_i2c_reva_regs_t *i2c)
{
    i2c->txctrl0 &= ~MXC_F_I2C_REVA_TXCTRL0_PRELOAD_MODE;
}

void MXC_I2C_RevA_EnableGeneralCall(mxc_i2c_reva_regs_t *i2c)
{
    i2c->ctrl |= MXC_F_I2C_REVA_CTRL_GC_ADDR_EN;
}

void MXC_I2C_RevA_DisableGeneralCall(mxc_i2c_reva_regs_t *i2c)
{
    i2c->ctrl &= ~MXC_F_I2C_REVA_CTRL_GC_ADDR_EN;
}

void MXC_I2C_RevA_SetTimeout(mxc_i2c_reva_regs_t *i2c, unsigned int timeout)
{
    i2c->timeout |= (timeout & 0xFFFF);
}

unsigned int MXC_I2C_RevA_GetTimeout(mxc_i2c_reva_regs_t *i2c)
{
    return (i2c->timeout & 0xFFFF);
}

/* ************************************************************************* */
/* Transaction level functions                                               */
/* ************************************************************************* */

int MXC_I2C_RevA_MasterTransaction(mxc_i2c_reva_req_t *req)
{
    mxc_i2c_reva_regs_t *i2c = req->i2c; // Save off pointer for faster access
    unsigned int written = 0;
    unsigned int read = 0;

    if (req->addr > MXC_I2C_REVA_MAX_ADDR_WIDTH) {
        return E_NOT_SUPPORTED;
    }

    if (MXC_I2C_GET_IDX((mxc_i2c_regs_t *)i2c) < 0) {
        return E_BAD_PARAM;
    }

    if (!(i2c->ctrl & MXC_F_I2C_REVA_CTRL_MST_MODE)) {
        return E_BAD_STATE;
    }

    // if(!read | write)
    //  Start
    //  send addr w/ write bit
    // if(Write)
    //  send tx_len data
    //  return if error (or NACK)
    // if(Read)
    //  if(Write)
    //   send restart
    //  else
    //   send start
    //  send addr w/ read bit
    //  read rx_len bytes acking all
    // stop or restart
    // return good or error

    MXC_I2C_ClearFlags((mxc_i2c_regs_t *)i2c, MXC_I2C_REVA_INTFL0_MASK,
                       MXC_I2C_REVA_INTFL1_MASK); // Clear all I2C Interrupts
    MXC_I2C_ClearTXFIFO((mxc_i2c_regs_t *)i2c);
    MXC_I2C_ClearRXFIFO((mxc_i2c_regs_t *)i2c);
    i2c->inten0 = 0;
    i2c->inten1 = 0;

    if ((req->rx_len == 0) || (req->tx_len != 0)) {
        // Load the slave address with write bit set
        i2c->fifo = (req->addr << 1) & ~0x1;
        i2c->mstctrl |= MXC_F_I2C_REVA_MSTCTRL_START;
    }

    while (req->tx_len > written) {
        if (i2c->intfl0 & MXC_F_I2C_REVA_INTFL0_TX_THD) {
            written += MXC_I2C_WriteTXFIFO((mxc_i2c_regs_t *)i2c, &req->tx_buf[written],
                                           req->tx_len - written);
            i2c->intfl0 = MXC_F_I2C_REVA_INTFL0_TX_THD;
        }

        if (i2c->intfl0 & MXC_I2C_REVA_ERROR) {
            req->tx_len = written;
            MXC_I2C_Stop((mxc_i2c_regs_t *)i2c);
            return E_COMM_ERR;
        }
    }

    MXC_I2C_ClearFlags((mxc_i2c_regs_t *)i2c,
                       MXC_F_I2C_REVA_INTFL0_DONE | MXC_F_I2C_REVA_INTFL0_RX_THD, 0);

    if (req->rx_len != 0) {
        if (req->rx_len > MXC_I2C_REVA_MAX_FIFO_TRANSACTION) {
            i2c->rxctrl1 = 0;
        } else {
            i2c->rxctrl1 = req->rx_len; // 0 for 256, otherwise number of bytes to read
        }

        MXC_I2C_Start((mxc_i2c_regs_t *)i2c); // Start or Restart as needed

        while (i2c->mstctrl & MXC_F_I2C_REVA_MSTCTRL_RESTART) {}

        i2c->fifo = (req->addr << 1) | 0x1; // Load slave address with read bit.
    }

    while (req->rx_len > read) {
        if (i2c->intfl0 & (MXC_F_I2C_REVA_INTFL0_RX_THD | MXC_F_I2C_REVA_INTFL0_DONE)) {
            read +=
                MXC_I2C_ReadRXFIFO((mxc_i2c_regs_t *)i2c, &req->rx_buf[read], req->rx_len - read);
            i2c->intfl0 = MXC_F_I2C_REVA_INTFL0_RX_THD;
        }

        if (i2c->intfl0 & MXC_I2C_REVA_ERROR) {
            req->rx_len = read;
            MXC_I2C_Stop((mxc_i2c_regs_t *)i2c);
            return E_COMM_ERR;
        }

        if ((i2c->intfl0 & MXC_F_I2C_REVA_INTFL0_DONE) && (req->rx_len < read)) {
            if ((req->rx_len - read) > MXC_I2C_REVA_MAX_FIFO_TRANSACTION) {
                i2c->rxctrl1 = 0;
            } else {
                i2c->rxctrl1 = (req->rx_len - read); // 0 for 256, otherwise number of bytes to read
            }

            i2c->mstctrl |= MXC_F_I2C_REVA_MSTCTRL_RESTART;
            i2c->intfl0 = MXC_F_I2C_REVA_INTFL0_DONE;
            i2c->fifo = (req->addr << 1) | 0x1; // Load slave address with read bit.
        }
    }

    if (req->restart) {
        i2c->mstctrl |= MXC_F_I2C_REVA_MSTCTRL_RESTART;
    } else {
        i2c->mstctrl |= MXC_F_I2C_REVA_MSTCTRL_STOP;

        while (!(i2c->intfl0 & MXC_F_I2C_REVA_INTFL0_STOP)) {}
        // Wait for Transaction to finish
    }

    while (!(i2c->intfl0 & MXC_F_I2C_REVA_INTFL0_DONE)) {}
    // Wait for Transaction to finish

    i2c->intfl0 = MXC_F_I2C_REVA_INTFL0_DONE | MXC_F_I2C_REVA_INTFL0_STOP;

    if (i2c->intfl0 & MXC_I2C_REVA_ERROR) {
        return E_COMM_ERR;
    }

    return E_NO_ERROR;
}

int MXC_I2C_RevA_MasterTransactionAsync(mxc_i2c_reva_req_t *req)
{
    int i2cNum = MXC_I2C_GET_IDX((mxc_i2c_regs_t *)(req->i2c));
    mxc_i2c_reva_regs_t *i2c = req->i2c;

    if (i2cNum < 0) {
        return E_BAD_PARAM;
    }

    if (!(i2c->ctrl & MXC_F_I2C_REVA_CTRL_MST_MODE)) {
        return E_BAD_STATE;
    }

    if (AsyncRequests[i2cNum] == NULL) {
        if (req->addr > MXC_I2C_REVA_MAX_ADDR_WIDTH) {
            return E_NOT_SUPPORTED;
        }

        AsyncRequests[i2cNum] = (void *)req;
        AsyncWritten[i2cNum] = 0;
        AsyncRead[i2cNum] = 0;
        MXC_I2C_ClearFlags((mxc_i2c_regs_t *)i2c, MXC_I2C_REVA_INTFL0_MASK,
                           MXC_I2C_REVA_INTFL1_MASK); // Clear all I2C Interrupts
        MXC_I2C_ClearTXFIFO((mxc_i2c_regs_t *)i2c);
        MXC_I2C_ClearRXFIFO((mxc_i2c_regs_t *)i2c);

        i2c->inten0 = MXC_I2C_REVA_ERROR;

        if (req->tx_len) {
            i2c->fifo = (req->addr << 1) & ~0x1; // Load the slave address with write bit set
        } else if (req->rx_len) {
            i2c->fifo = (req->addr << 1) | 0x1; // Load the slave address with read bit set

            /* Set the number of bytes to read */
            if (req->rx_len > MXC_I2C_REVA_MAX_FIFO_TRANSACTION) {
                i2c->rxctrl1 = 0;
            } else {
                i2c->rxctrl1 = req->rx_len; // 0 for 256, otherwise number of bytes to read
            }

            /* Enable RX Threshold interrupt for when the FIFO is full */
            i2c->inten0 |= (MXC_F_I2C_REVA_INTEN0_RX_THD | MXC_F_I2C_REVA_INTEN0_DONE);
        } else {
            /* Must have tx_len and/or rx_len */
            return E_BAD_PARAM;
        }

        MXC_I2C_Start((mxc_i2c_regs_t *)i2c);

        /* Fill the FIFO as nessary */
        MXC_I2C_RevA_MasterAsyncHandler(i2cNum);

        return E_NO_ERROR;
    } else {
        return E_BUSY;
    }
}

int MXC_I2C_RevA_MasterTransactionDMA(mxc_i2c_reva_req_t *req, mxc_dma_regs_t *dma)
{
    int i2cNum;

    mxc_i2c_reva_regs_t *i2c = req->i2c; // Save off pointer for faster access
    i2cNum = MXC_I2C_GET_IDX((mxc_i2c_regs_t *)i2c);

    if (req->addr > MXC_I2C_REVA_MAX_ADDR_WIDTH) {
        return E_NOT_SUPPORTED;
    }

    if (i2cNum < 0) {
        return E_BAD_PARAM;
    }

    if (!(i2c->ctrl & MXC_F_I2C_REVA_CTRL_MST_MODE)) {
        return E_BAD_STATE;
    }

    if (req->rx_len > MXC_I2C_REVA_MAX_FIFO_TRANSACTION) {
        return E_BAD_PARAM;
    }

    MXC_I2C_ClearFlags((mxc_i2c_regs_t *)i2c, MXC_I2C_REVA_INTFL0_MASK,
                       MXC_I2C_REVA_INTFL1_MASK); // Clear all I2C Interrupts
    MXC_I2C_ClearTXFIFO((mxc_i2c_regs_t *)i2c);
    MXC_I2C_ClearRXFIFO((mxc_i2c_regs_t *)i2c);

    MXC_I2C_SetTXThreshold((mxc_i2c_regs_t *)i2c, 2);
    MXC_I2C_SetRXThreshold((mxc_i2c_regs_t *)i2c, 1);

    states[i2cNum].req = req;

    states[i2cNum].channelTx = 0xFF;
    states[i2cNum].channelRx = 0xFF;

#if TARGET_NUM == 32665
    MXC_DMA_Init(dma);
#else
    MXC_DMA_Init();
#endif

    //tx
    if ((req->tx_buf != NULL) && !(states[i2cNum].writeDone)) {
        i2c->fifo = ((req->addr) << 1) & ~0x1; // Load the slave address with write bit set

#if TARGET_NUM == 32665
        MXC_I2C_WriteTXFIFODMA((mxc_i2c_regs_t *)i2c, req->tx_buf, req->tx_len, NULL, dma);
#else
        MXC_I2C_WriteTXFIFODMA((mxc_i2c_regs_t *)i2c, req->tx_buf, req->tx_len, NULL);
#endif
    } else {
        states[i2cNum].writeDone = 1;
    }

    if (req->rx_buf != NULL) {
        while (states[i2cNum].writeDone != 1) {}
        //Ensure DMA transmit has finished before attempting to receive

        if ((states[i2cNum].writeDone) && (!states[i2cNum].readDone)) {
            if (req->rx_len > MXC_I2C_REVA_MAX_FIFO_TRANSACTION) {
                i2c->rxctrl1 = 0;
            } else {
                i2c->rxctrl1 = req->rx_len; // 0 for 256, otherwise number of bytes to read
            }

            MXC_I2C_Start((mxc_i2c_regs_t *)i2c); // Start or Restart as needed

            while (i2c->mstctrl & MXC_F_I2C_REVA_MSTCTRL_RESTART) {}

            i2c->fifo = ((req->addr) << 1) | 0x1; // Load the slave address with write bit set

#if TARGET_NUM == 32665
            MXC_I2C_ReadRXFIFODMA((mxc_i2c_regs_t *)i2c, req->rx_buf, req->rx_len, NULL, dma);
#else
            MXC_I2C_ReadRXFIFODMA((mxc_i2c_regs_t *)i2c, req->rx_buf, req->rx_len, NULL);
#endif
        }
    } else {
        states[i2cNum].readDone = 1;
    }

    return E_NO_ERROR;
}

void MXC_I2C_RevA_DMACallback(int ch, int error)
{
    mxc_i2c_reva_req_t *temp_req;

    for (int i = 0; i < MXC_I2C_INSTANCES; i++) {
        if (states[i].channelTx == ch) {
            //save the request
            temp_req = states[i].req;
            states[i].writeDone = 1;

            if (states[i].readDone) {
                if (temp_req->restart) {
                    (temp_req->i2c)->mstctrl |= MXC_F_I2C_REVA_MSTCTRL_RESTART;
                } else {
                    (temp_req->i2c)->mstctrl |= MXC_F_I2C_REVA_MSTCTRL_STOP;
                }

                MXC_DMA_ReleaseChannel(states[i].channelRx);
                MXC_DMA_ReleaseChannel(states[i].channelTx);

                // Callback if not NULL
                if (temp_req->callback != NULL) {
                    temp_req->callback(temp_req, E_NO_ERROR);
                }
            }
        } else if (states[i].channelRx == ch) {
            //save the request
            states[i].readDone = 1;
            temp_req = states[i].req;

            if (states[i].writeDone) {
                if (temp_req->restart) {
                    (temp_req->i2c)->mstctrl |= MXC_F_I2C_REVA_MSTCTRL_RESTART;
                } else {
                    (temp_req->i2c)->mstctrl |= MXC_F_I2C_REVA_MSTCTRL_STOP;
                }

                MXC_DMA_ReleaseChannel(states[i].channelRx);
                MXC_DMA_ReleaseChannel(states[i].channelTx);

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

int MXC_I2C_RevA_SlaveTransaction(mxc_i2c_reva_regs_t *i2c, mxc_i2c_reva_slave_handler_t callback,
                                  uint32_t interruptCheck)
{
    int retVal = E_NO_ERROR;

    uint32_t int_en[2];
    int_en[0] = interruptCheck;
    int_en[1] = 0;

    if (MXC_I2C_GET_IDX((mxc_i2c_regs_t *)i2c) < 0) {
        return E_BAD_PARAM;
    }

    if (i2c->ctrl & MXC_F_I2C_REVA_CTRL_MST_MODE) {
        return E_BAD_STATE;
    }

    MXC_I2C_ClearFlags((mxc_i2c_regs_t *)i2c, MXC_I2C_REVA_INTFL0_MASK,
                       MXC_I2C_REVA_INTFL1_MASK); // Clear all I2C Interrupts
    MXC_I2C_ClearTXFIFO((mxc_i2c_regs_t *)i2c);
    MXC_I2C_ClearRXFIFO((mxc_i2c_regs_t *)i2c);

    // Callback called on
    // Slave Address Match (distinguish read/write)
    // RX Threshold
    // TX Threshold
    // Done
    // TX Underflow
    // RX Overflow
    //
    // Event Codes
    // I2C_EVT_MASTER_WR
    // I2C_EVT_MASTER_RD
    // I2C_EVT_RX_THRESH
    // I2C_EVT_TX_THRESH
    // I2C_EVT_TRANS_COMP
    // I2C_EVT_UNDERFLOW
    // I2C_EVT_OVERFLOW

    while (int_en[0] > 0 || int_en[1] > 0) {
        MXC_I2C_RevA_SlaveAsyncHandler(i2c, callback, int_en, &retVal);
    }

    return retVal;
}

int MXC_I2C_RevA_SlaveTransactionAsync(mxc_i2c_reva_regs_t *i2c,
                                       mxc_i2c_reva_slave_handler_t callback,
                                       uint32_t interruptCheck)
{
    int i2cnum = MXC_I2C_GET_IDX((mxc_i2c_regs_t *)i2c);

    if (i2cnum < 0) {
        return E_BAD_PARAM;
    }

    if (i2c->ctrl & MXC_F_I2C_REVA_CTRL_MST_MODE) {
        return E_BAD_STATE;
    }

    if (AsyncRequests[i2cnum] != NULL) {
        return E_BUSY;
    }

    MXC_I2C_ClearFlags((mxc_i2c_regs_t *)i2c, MXC_I2C_REVA_INTFL0_MASK,
                       MXC_I2C_REVA_INTFL1_MASK); // Clear all I2C Interrupts
    MXC_I2C_ClearTXFIFO((mxc_i2c_regs_t *)i2c);
    MXC_I2C_ClearRXFIFO((mxc_i2c_regs_t *)i2c);
    MXC_I2C_SetTXThreshold((mxc_i2c_regs_t *)i2c, 1); // set TX threshold to 2 bytes
    MXC_I2C_SetRXThreshold((mxc_i2c_regs_t *)i2c, 1); // set RX threshold to 6 bytes
    AsyncRequests[i2cnum] = (void *)callback;

    i2c->inten0 = interruptCheck;

    return E_NO_ERROR;
}

int MXC_I2C_RevA_SetRXThreshold(mxc_i2c_reva_regs_t *i2c, unsigned int numBytes)
{
    unsigned int rxFIFOlen = (i2c->fifolen & MXC_F_I2C_REVA_FIFOLEN_RX_DEPTH) >>
                             MXC_F_I2C_REVA_FIFOLEN_RX_DEPTH_POS;

    if (numBytes > rxFIFOlen) {
        return E_BAD_PARAM;
    }

    i2c->rxctrl0 = (i2c->rxctrl0 & ~MXC_F_I2C_REVA_RXCTRL0_THD_LVL) |
                   (numBytes << MXC_F_I2C_REVA_RXCTRL0_THD_LVL_POS);
    return E_NO_ERROR;
}

unsigned int MXC_I2C_RevA_GetRXThreshold(mxc_i2c_reva_regs_t *i2c)
{
    return (i2c->rxctrl0 & MXC_F_I2C_REVA_RXCTRL0_THD_LVL) >> MXC_F_I2C_REVA_RXCTRL0_THD_LVL_POS;
}

int MXC_I2C_RevA_SetTXThreshold(mxc_i2c_reva_regs_t *i2c, unsigned int numBytes)
{
    unsigned int txFIFOlen = (i2c->fifolen & MXC_F_I2C_REVA_FIFOLEN_TX_DEPTH) >>
                             MXC_F_I2C_REVA_FIFOLEN_TX_DEPTH_POS;

    if (numBytes > txFIFOlen) {
        return E_BAD_PARAM;
    }

    i2c->txctrl0 = (i2c->txctrl0 & ~MXC_F_I2C_REVA_TXCTRL0_THD_LVL) |
                   (numBytes << MXC_F_I2C_REVA_TXCTRL0_THD_LVL_POS);
    return E_NO_ERROR;
}

unsigned int MXC_I2C_RevA_GetTXThreshold(mxc_i2c_reva_regs_t *i2c)
{
    return (i2c->txctrl0 & MXC_F_I2C_REVA_TXCTRL0_THD_LVL) >> MXC_F_I2C_REVA_TXCTRL0_THD_LVL_POS;
}

void MXC_I2C_RevA_AsyncCallback(mxc_i2c_reva_regs_t *i2c, int retVal)
{
    // Don't need to check for return value as this function is not accessible to user
    // i2c is already cheked for NULL from where this function is being called
    mxc_i2c_reva_req_t *req =
        (mxc_i2c_reva_req_t *)AsyncRequests[MXC_I2C_GET_IDX((mxc_i2c_regs_t *)i2c)];

    if (req->callback != NULL) {
        req->callback(req, retVal);
    }
}

void MXC_I2C_RevA_AsyncStop(mxc_i2c_reva_regs_t *i2c)
{
    /* Disable and clear interrupts */
    i2c->inten0 = 0;
    i2c->inten1 = 0;

    i2c->intfl0 = i2c->intfl0;
    i2c->intfl1 = i2c->intfl1;

    // Don't need to check for return value as this function is not accessible to user
    // i2c is already cheked for NULL from where this function is being called
    AsyncRequests[MXC_I2C_GET_IDX((mxc_i2c_regs_t *)i2c)] = NULL;
}

void MXC_I2C_RevA_AbortAsync(mxc_i2c_reva_regs_t *i2c)
{
    // Don't need to check for return value as this function is not accessible to user
    // i2c is already cheked for NULL from where this function is being called
    MXC_I2C_RevA_AsyncCallback(i2c, E_ABORT);
    MXC_I2C_RevA_AsyncStop(i2c);
}

void MXC_I2C_RevA_MasterAsyncHandler(int i2cNum)
{
    unsigned int written = AsyncWritten[i2cNum];
    unsigned int read = AsyncRead[i2cNum];
    mxc_i2c_reva_regs_t *i2c = (mxc_i2c_reva_regs_t *)MXC_I2C_GET_BASE(i2cNum);
    mxc_i2c_reva_req_t *req = (mxc_i2c_reva_req_t *)AsyncRequests[i2cNum];

    /* Check for errors */
    if (i2c->intfl0 & MXC_I2C_REVA_ERROR) {
        /* Clear and disable interrupts */
        i2c->intfl0 = i2c->intfl0;
        i2c->intfl1 = i2c->intfl1;
        i2c->inten0 = 0;
        i2c->inten1 = 0;

        MXC_I2C_Stop((mxc_i2c_regs_t *)i2c);
        MXC_I2C_RevA_AsyncCallback(i2c, E_COMM_ERR);
        MXC_I2C_RevA_AsyncStop(i2c);

        return;
    }

    /* Write data to the TX FIFO */
    if (req->tx_len > written) {
        if (i2c->intfl0 & MXC_F_I2C_REVA_INTFL0_TX_THD) {
            written += MXC_I2C_WriteTXFIFO((mxc_i2c_regs_t *)i2c, &req->tx_buf[written],
                                           req->tx_len - written);
            i2c->intfl0 = MXC_F_I2C_REVA_INTFL0_TX_THD;
        }

        /* Enable the TX Threshold interrupt if we still need to write to the TX FIFO */
        if (written < req->tx_len) {
            i2c->inten0 |= MXC_F_I2C_REVA_INTEN0_TX_THD;
        } else {
            i2c->inten0 &= ~(MXC_F_I2C_REVA_INTEN0_TX_THD);
        }

        /* Send a restart if we're reading after writing */
        if ((req->tx_len == written) && (req->rx_len)) {
            i2c->mstctrl |= MXC_F_I2C_REVA_MSTCTRL_RESTART;
            i2c->intfl0 = MXC_F_I2C_REVA_INTFL0_DONE;
            i2c->inten0 |= (MXC_F_I2C_REVA_INTEN0_DONE);
        }
    }

    /* Read data in the RX FIFO */
    if (req->rx_len > read) {
        if (i2c->intfl0 & (MXC_F_I2C_REVA_INTFL0_RX_THD | MXC_F_I2C_REVA_INTFL0_DONE)) {
            read +=
                MXC_I2C_ReadRXFIFO((mxc_i2c_regs_t *)i2c, &req->rx_buf[read], req->rx_len - read);
            i2c->intfl0 = MXC_F_I2C_REVA_INTFL0_RX_THD;
        }
    }

    /* Done writing, still reading */
    if ((req->tx_len == written) && (req->rx_len - read) &&
        (i2c->intfl0 & MXC_F_I2C_REVA_INTFL0_DONE)) {
        i2c->intfl0 = MXC_F_I2C_REVA_INTFL0_DONE;

        /* First done interrupt after completing writes to the TX FIFO */
        if (read == 0) {
            i2c->fifo = (req->addr << 1) | 0x1; // Load slave address with read bit.
        }

        /* Set the number of bytes to read */
        if ((req->rx_len - read) > MXC_I2C_REVA_MAX_FIFO_TRANSACTION) {
            i2c->rxctrl1 = 0;
        } else {
            i2c->rxctrl1 = (req->rx_len - read); // 0 for 256, otherwise number of bytes to read
        }

        /* Enable RX Threshold interrupt for when the FIFO is full */
        if (read < req->rx_len) {
            i2c->inten0 |= (MXC_F_I2C_REVA_INTEN0_RX_THD | MXC_F_I2C_REVA_INTEN0_DONE);
        } else {
            i2c->inten0 &= ~(MXC_F_I2C_REVA_INTEN0_RX_THD | MXC_F_I2C_REVA_INTEN0_DONE);
        }
    }

    /* Done reading and writing */
    if ((req->tx_len == written) && (req->rx_len == read)) {
        /* Disable and clear interrupts */
        i2c->inten0 = 0;
        i2c->inten1 = 0;
        i2c->intfl0 = i2c->intfl0;
        i2c->intfl1 = i2c->intfl1;

        /* Send a restart or stop at the end of the transaction */
        if (req->restart) {
            i2c->mstctrl |= MXC_F_I2C_REVA_MSTCTRL_RESTART;
        } else {
            i2c->mstctrl |= MXC_F_I2C_REVA_MSTCTRL_STOP;
        }

        /* Call the callback */
        if (i2c->intfl0 & MXC_I2C_REVA_ERROR) {
            MXC_I2C_RevA_AsyncCallback(i2c, E_COMM_ERR);
        } else {
            MXC_I2C_RevA_AsyncCallback(i2c, E_NO_ERROR);
        }

        /* Clear the async state */
        MXC_I2C_RevA_AsyncStop(i2c);

    } else {
        AsyncWritten[i2cNum] = written;
        AsyncRead[i2cNum] = read;
    }
}

void MXC_I2C_RevA_SlaveAsyncHandler(mxc_i2c_reva_regs_t *i2c, mxc_i2c_reva_slave_handler_t callback,
                                    uint32_t *int_en, int *retVal)
{
    uint32_t tFlags = i2c->intfl0;
    *retVal = E_NO_ERROR;

    uint32_t readFlag = i2c->ctrl & MXC_F_I2C_REVA_CTRL_READ;
    // Callback called on
    // Slave Address Match (distinguish read/write)
    // RX Threshold
    // TX Threshold
    // Done
    // TX Underflow
    // RX Overflow
    //
    // Event Codes
    // I2C_EVT_MASTER_WR
    // I2C_EVT_MASTER_RD
    // I2C_EVT_RX_THRESH
    // I2C_EVT_TX_THRESH
    // I2C_EVT_TRANS_COMP
    // I2C_EVT_UNDERFLOW
    // I2C_EVT_OVERFLOW
    if (!(int_en[0] & (MXC_F_I2C_REVA_INTFL0_RD_ADDR_MATCH | MXC_F_I2C_REVA_INTFL0_WR_ADDR_MATCH |
                       MXC_F_I2C_REVA_INTFL0_ADDR_MATCH))) {
        // The STOPERR/STARTERR interrupt that's enabled here could fire before we are addressed
        // (fires anytime a stop/start is detected out of sequence).
        if (tFlags & MXC_I2C_REVA_ERROR) {
            *retVal = E_COMM_ERR;

            if (callback != NULL) {
                callback(i2c, MXC_I2C_REVA_EVT_TRANS_COMP, retVal);
            }

            MXC_I2C_ClearFlags((mxc_i2c_regs_t *)i2c, MXC_I2C_REVA_INTFL0_MASK,
                               MXC_I2C_REVA_INTFL1_MASK); // Clear all I2C Interrupts
            MXC_I2C_ClearTXFIFO((mxc_i2c_regs_t *)i2c);
            MXC_I2C_ClearRXFIFO((mxc_i2c_regs_t *)i2c);
            int_en[0] = 0;
            int_en[1] = 0;
            AsyncRequests[MXC_I2C_GET_IDX((mxc_i2c_regs_t *)i2c)] = NULL;
        }

        if (int_en[0] & MXC_F_I2C_REVA_INTFL0_RX_THD || int_en[1] & MXC_F_I2C_REVA_INTFL1_RX_OV) {
            if (tFlags & MXC_F_I2C_REVA_INTFL0_RX_THD) {
                if (callback != NULL) {
                    callback(i2c, MXC_I2C_REVA_EVT_RX_THRESH, NULL);
                }

                i2c->intfl0 = MXC_F_I2C_REVA_INTFL0_RX_THD;
            }

            if (i2c->intfl1 & MXC_F_I2C_REVA_INTFL1_RX_OV) {
                if (callback != NULL) {
                    callback(i2c, MXC_I2C_REVA_EVT_OVERFLOW, NULL);
                }

                i2c->intfl1 = MXC_F_I2C_REVA_INTFL1_RX_OV;
            }
        }

        if (int_en[0] & (MXC_F_I2C_REVA_INTFL0_TX_THD | MXC_F_I2C_REVA_INTFL0_TX_LOCKOUT) ||
            int_en[1] & MXC_F_I2C_REVA_INTFL1_TX_UN) {
            if (tFlags & MXC_F_I2C_REVA_INTFL0_TX_THD) {
                if (callback != NULL) {
                    callback(i2c, MXC_I2C_REVA_EVT_TX_THRESH, NULL);
                }

                i2c->intfl0 = MXC_F_I2C_REVA_INTFL0_TX_THD;
            }

            if (i2c->intfl1 & MXC_F_I2C_REVA_INTFL1_TX_UN) {
                if (callback != NULL) {
                    callback(i2c, MXC_I2C_REVA_EVT_UNDERFLOW, NULL);
                }

                i2c->intfl1 = MXC_F_I2C_REVA_INTFL1_TX_UN;
            }

            if (tFlags & MXC_F_I2C_REVA_INTFL0_TX_LOCKOUT) {
                *retVal = E_NO_ERROR;

                if (callback != NULL) {
                    callback(i2c, MXC_I2C_REVA_EVT_TRANS_COMP, retVal);
                }

                i2c->intfl0 = MXC_F_I2C_REVA_INTFL0_TX_LOCKOUT;
                int_en[0] = 0;
                int_en[1] = 0;
                AsyncRequests[MXC_I2C_GET_IDX((mxc_i2c_regs_t *)i2c)] = NULL;
            }
        }

        if (tFlags & MXC_F_I2C_REVA_INTFL0_STOP) {
            *retVal = E_NO_ERROR;

            if (callback != NULL) {
                callback(i2c, MXC_I2C_REVA_EVT_TRANS_COMP, retVal);
            }

            i2c->intfl0 = MXC_F_I2C_REVA_INTFL0_STOP;
            int_en[0] = 0;
            int_en[1] = 0;
            AsyncRequests[MXC_I2C_GET_IDX((mxc_i2c_regs_t *)i2c)] = NULL;
        }
    }

    if (tFlags & MXC_F_I2C_REVA_INTFL0_RD_ADDR_MATCH) {
        if (callback != NULL) {
            callback(i2c, MXC_I2C_REVA_EVT_MASTER_WR, NULL);
        }

        i2c->intfl0 = MXC_F_I2C_REVA_INTFL0_RD_ADDR_MATCH;
        i2c->intfl0 = MXC_F_I2C_REVA_INTFL0_ADDR_MATCH;
        i2c->intfl0 = MXC_F_I2C_REVA_INTFL0_TX_LOCKOUT;

        int_en[0] = MXC_F_I2C_REVA_INTFL0_RX_THD | MXC_F_I2C_REVA_INTFL0_DONE | MXC_I2C_REVA_ERROR;
        int_en[1] = MXC_F_I2C_REVA_INTFL1_RX_OV;
    }

    if (tFlags & MXC_F_I2C_REVA_INTFL0_WR_ADDR_MATCH) {
        if (callback != NULL) {
            callback(i2c, MXC_I2C_REVA_EVT_MASTER_RD, NULL);
        }

        i2c->intfl0 = MXC_F_I2C_REVA_INTFL0_WR_ADDR_MATCH;
        i2c->intfl0 = MXC_F_I2C_REVA_INTFL0_ADDR_MATCH;
        int_en[0] = MXC_F_I2C_REVA_INTFL0_TX_THD | MXC_F_I2C_REVA_INTFL0_TX_LOCKOUT |
                    MXC_I2C_REVA_ERROR;
        int_en[1] = MXC_F_I2C_REVA_INTFL1_TX_UN;
    }

    if (tFlags & MXC_F_I2C_REVA_INTFL0_ADDR_MATCH) {
        if (readFlag & MXC_F_I2C_REVA_CTRL_READ) {
            if (callback != NULL) {
                callback(i2c, MXC_I2C_REVA_EVT_MASTER_RD, NULL);
            }

            i2c->intfl0 = MXC_F_I2C_REVA_INTFL0_RD_ADDR_MATCH;
            i2c->intfl0 = MXC_F_I2C_REVA_INTFL0_ADDR_MATCH;
            i2c->intfl0 = MXC_F_I2C_REVA_INTFL0_TX_LOCKOUT;

            int_en[0] = MXC_F_I2C_REVA_INTFL0_TX_THD | MXC_F_I2C_REVA_INTFL0_TX_LOCKOUT |
                        MXC_I2C_REVA_ERROR;
            int_en[1] = MXC_F_I2C_REVA_INTFL1_TX_UN;
        } else {
            if (callback != NULL) {
                callback(i2c, MXC_I2C_REVA_EVT_MASTER_WR, NULL);
            }

            i2c->intfl0 = MXC_F_I2C_REVA_INTFL0_WR_ADDR_MATCH;
            i2c->intfl0 = MXC_F_I2C_REVA_INTFL0_ADDR_MATCH;
            int_en[0] = MXC_F_I2C_REVA_INTFL0_RX_THD | MXC_F_I2C_REVA_INTFL0_DONE |
                        MXC_I2C_REVA_ERROR;
            int_en[1] = MXC_F_I2C_REVA_INTFL1_RX_OV;
        }
    } else if (tFlags & MXC_I2C_REVA_ERROR) {
        *retVal = E_COMM_ERR;

        if (callback != NULL) {
            callback(i2c, MXC_I2C_REVA_EVT_TRANS_COMP, retVal);
        }

        MXC_I2C_RevA_ClearFlags(i2c, MXC_I2C_REVA_INTFL0_MASK,
                                MXC_I2C_REVA_INTFL1_MASK); // clear all i2c interrupts
        MXC_I2C_RevA_ClearTXFIFO(i2c);
        MXC_I2C_RevA_ClearRXFIFO(i2c);
        int_en[0] = 0;
        int_en[1] = 0;
        AsyncRequests[MXC_I2C_GET_IDX((mxc_i2c_regs_t *)i2c)] = NULL;
    }
}

void MXC_I2C_RevA_AsyncHandler(mxc_i2c_reva_regs_t *i2c, uint32_t interruptCheck)
{
    int i2cNum = MXC_I2C_GET_IDX((mxc_i2c_regs_t *)i2c);
    int slaveRetVal;
    uint32_t int_en[2];

    if (i2cNum < 0) {
        return;
    }

    if (i2c->ctrl & MXC_F_I2C_REVA_CTRL_MST_MODE) {
        MXC_I2C_RevA_MasterAsyncHandler(i2cNum);
    } else {
        mxc_i2c_reva_slave_handler_t callback = (mxc_i2c_reva_slave_handler_t)AsyncRequests[i2cNum];

        int_en[0] = i2c->inten0;
        int_en[1] = i2c->inten1;

        MXC_I2C_RevA_SlaveAsyncHandler(i2c, callback, int_en, &slaveRetVal);

        i2c->inten0 = int_en[0];
        i2c->inten1 = int_en[1];
    }
}