/* Copyright Statement:
 *
 * This software/firmware and related documentation ("AutoChips Software") are
 * protected under relevant copyright laws. The information contained herein is
 * confidential and proprietary to AutoChips Inc. and/or its licensors. Without
 * the prior written permission of AutoChips inc. and/or its licensors, any
 * reproduction, modification, use or disclosure of AutoChips Software, and
 * information contained herein, in whole or in part, shall be strictly
 * prohibited.
 *
 * AutoChips Inc. (C) 2022. All rights reserved.
 *
 * BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES
 * THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("AUTOCHIPS SOFTWARE")
 * RECEIVED FROM AUTOCHIPS AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER
 * ON AN "AS-IS" BASIS ONLY. AUTOCHIPS EXPRESSLY DISCLAIMS ANY AND ALL
 * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
 * NONINFRINGEMENT. NEITHER DOES AUTOCHIPS PROVIDE ANY WARRANTY WHATSOEVER WITH
 * RESPECT TO THE SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY,
 * INCORPORATED IN, OR SUPPLIED WITH THE AUTOCHIPS SOFTWARE, AND RECEIVER AGREES
 * TO LOOK ONLY TO SUCH THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO.
 * RECEIVER EXPRESSLY ACKNOWLEDGES THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO
 * OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES CONTAINED IN AUTOCHIPS
 * SOFTWARE. AUTOCHIPS SHALL ALSO NOT BE RESPONSIBLE FOR ANY AUTOCHIPS SOFTWARE
 * RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR
 * STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND AUTOCHIPS'S
 * ENTIRE AND CUMULATIVE LIABILITY WITH RESPECT TO THE AUTOCHIPS SOFTWARE
 * RELEASED HEREUNDER WILL BE, AT AUTOCHIPS'S OPTION, TO REVISE OR REPLACE THE
 * AUTOCHIPS SOFTWARE AT ISSUE, OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE
 * CHARGE PAID BY RECEIVER TO AUTOCHIPS FOR SUCH AUTOCHIPS SOFTWARE AT ISSUE.
 */

/*!
 * @file ac780x_i2c.c
 *
 * @brief This file provides i2c integration functions.
 *
 */

/* ===========================================  Includes  =========================================== */
#include "ac780x_i2c_reg.h"

/* ============================================  Define  ============================================ */

/* ===========================================  Typedef  ============================================ */

/* ==========================================  Variables  =========================================== */
/* i2c info */
const I2C_INFOType g_i2cInfoPool[] =
{
    {I2C0, I2C0_IRQn, CLK_I2C0, SRST_I2C0},
};

/* I2C callback pointer array */
static DeviceCallback_Type s_i2cCallback[I2C_INSTANCE_MAX] = {(DeviceCallback_Type)NULL};

#ifdef I2C_USE_INTERRUPT_TRANSMIT

/* transmit information struct */
I2C_TransmitType g_i2cTransmitInfo[I2C_INSTANCE_MAX] = {0};
/* transmit status enum */
uint32_t g_i2cStatus[I2C_INSTANCE_MAX] = {I2C_TRANSMIT_STATUS_ERROR_NULL};
/* internal callback function */
static void I2C_MasterSlaveIntHandler(void *device, uint32_t wparam, uint32_t lparam);

#endif /* I2C_USE_INTERRUPT_TRANSMIT */

/* ====================================  Functions declaration  ===================================== */

/* ======================================  Functions define  ======================================== */
/*!
* @brief Initialize I2C module
*
* @param[in] I2Cx: i2c module
*               - I2C0
* @param[in] config: I2C configuration pointer
* @return none
*/
void I2C_Init(I2C_Type *I2Cx, const I2C_ConfigType *config)
{
    uint8_t instance = I2C_INDEX(I2Cx);

    DEVICE_ASSERT(I2C_INSTANCE_MAX > instance);
    DEVICE_ASSERT(NULL != config);

    /* i2c module system enable */
    CKGEN_Enable(g_i2cInfoPool[instance].CKGEN_I2Cx, ENABLE);
    CKGEN_SoftReset(g_i2cInfoPool[instance].SRST_I2Cx, ENABLE);

    /* master scl sample, halfbaud setting */
    I2C_SetSampleStep(I2Cx, config->masterConfig.sampleCnt, config->masterConfig.stepCnt);

    /* master scl sync, arbitration */
    I2C_SetSYNC(I2Cx, config->masterConfig.SYNCEn);
    I2C_SetARB(I2Cx, config->masterConfig.ARBEn);

    /* slave 7bit, 7bit range address */
    I2C_SetRAD(I2Cx, config->slaveConfig.rangeAddrEn);
    I2C_SetSlaveAddr(I2Cx, config->slaveConfig.slaveAddr);
    I2C_SetSlaveRangeAddr(I2Cx, config->slaveConfig.slaveRangeAddr);

    /* master/slave 10bit address */
    I2C_SetADEXT(I2Cx, config->extAddrEn);

    /* slave stretch */
    I2C_SetStretch(I2Cx, config->slaveConfig.stretchEn);

    /* slave monitor */
    I2C_SetMNT(I2Cx, config->slaveConfig.monitorEn);

    /* slave general call */
    I2C_SetGCA(I2Cx, config->slaveConfig.generalCallEn);

    /* slave wakeup */
    I2C_SetWakeup(I2Cx, config->slaveConfig.wakeupEn);

    /* tx underflow, rx overflow int */
    I2C_SetTxUFInterrupt(I2Cx, config->slaveConfig.txUFInterruptEn);
    I2C_SetRxOFInterrupt(I2Cx, config->slaveConfig.rxOFInterruptEn);

    /* i2c deglitch cnt */
    I2C_SetDGLCnt(I2Cx, config->slaveConfig.glitchFilterCnt);

    /* i2c start stop int */
    I2C_SetSSInterrupt(I2Cx, config->ssInterruptEn);

    /* i2c nack int */
    I2C_SetNackInterrupt(I2Cx, config->nackInterruptEn);

    /* i2c master/slave */
    I2C_SetMSTR(I2Cx, config->mode);

    /* i2c module enable */
    I2C_SetModuleEnable(I2Cx, config->i2cEn);

    /* i2c callback function */
    s_i2cCallback[instance] = config->callBack;

    /* i2c int */
    I2C_SetInterrupt(I2Cx, config->interruptEn);

    /* i2c NVIC */
    if (DISABLE == config->interruptEn)
    {
        NVIC_DisableIRQ(g_i2cInfoPool[instance].I2Cx_IRQn);
        NVIC_ClearPendingIRQ(g_i2cInfoPool[instance].I2Cx_IRQn);
    }
    else
    {
        NVIC_EnableIRQ(g_i2cInfoPool[instance].I2Cx_IRQn);
    }
}

/*!
* @brief I2C De-initialize
*
* @param[in] I2Cx: i2c module
*               - I2C0
* @param[in] config: I2C configuration pointer
* @return none
*/
void I2C_DeInit(I2C_Type *I2Cx)
{
    uint8_t instance = I2C_INDEX(I2Cx);

    DEVICE_ASSERT(I2C_INSTANCE_MAX > instance);

    s_i2cCallback[instance] = (DeviceCallback_Type)NULL;
    NVIC_DisableIRQ(g_i2cInfoPool[instance].I2Cx_IRQn);
    NVIC_ClearPendingIRQ(g_i2cInfoPool[instance].I2Cx_IRQn);
    CKGEN_SoftReset(g_i2cInfoPool[instance].SRST_I2Cx, DISABLE);
    CKGEN_Enable(g_i2cInfoPool[instance].CKGEN_I2Cx, DISABLE);
}

/*!
* @brief set I2C Callback function
*
* @param[in] I2Cx: i2c module
*               - I2C0
* @param[in] callback: call back function
* @return none
*/
void I2C_SetCallback(I2C_Type *I2Cx, DeviceCallback_Type callback)
{
    uint8_t instance = I2C_INDEX(I2Cx);

    DEVICE_ASSERT(I2C_INSTANCE_MAX > instance);

    s_i2cCallback[instance] = callback;
}

/*!
* @brief I2C0 interrupt request handler
*
* @param[in] none
* @return none
*/
void I2C0_IRQHandler(void)
{
    uint32_t wpara = 0, lpara = 0;

    wpara = I2C_GetStatus0(I2C0);
    lpara = I2C_GetStatus1(I2C0);

#ifdef I2C_USE_INTERRUPT_TRANSMIT
    I2C_MasterSlaveIntHandler(I2C0, wpara, lpara);
#endif /* I2C_USE_INTERRUPT_TRANSMIT */

    if (NULL != s_i2cCallback[0])
    {
        s_i2cCallback[0](I2C0, wpara, lpara);
    }
}

/*!
* @brief I2C send start
*
* @param[in] I2Cx: I2C module
*               - I2C0
* @return i2c hardware status
*/
uint32_t I2C_Start(I2C_Type *I2Cx)
{
    uint32_t timeout = 0;
    uint32_t status = I2C_HW_STATUS_ERROR_NULL;

    DEVICE_ASSERT(I2C_INSTANCE_MAX > I2C_INDEX(I2Cx));

    I2C_TxEn(I2Cx);
    I2C_SendStart(I2Cx);

    /* wait busy status */
    while ((!I2C_IsBusy(I2Cx)) && (I2C_HW_DEADLINE_TIMEOUT > timeout++));
    if (I2C_HW_DEADLINE_TIMEOUT <= timeout)
    {
        status |= I2C_HW_STATUS_START_TO_BUSY_TIMEOUT;
    }
    I2C_ClearStartFlag(I2Cx);

    /* wait core ready */
    timeout = 0;
    while ((!I2C_IsReady(I2Cx)) && (I2C_HW_DEADLINE_TIMEOUT > timeout++));
    if (I2C_HW_DEADLINE_TIMEOUT <= timeout)
    {
        status |= I2C_HW_STATUS_CORE_READY_TIMROUT;
    }

    return status;
}

/*!
* @brief I2C send re-start
*        make sure to disable start/stop interrupt when use the interface
*
* @param[in] I2Cx: I2C module
*               - I2C0
* @return i2c hardware status
*/
uint32_t I2C_Restart(I2C_Type *I2Cx)
{
    uint32_t timeout = 0;
    uint32_t status = I2C_HW_STATUS_ERROR_NULL;

    DEVICE_ASSERT(I2C_INSTANCE_MAX > I2C_INDEX(I2Cx));

    I2C_ClearStartFlag(I2Cx);
    I2C_SendStart(I2Cx);

    /* wait start status */
    while ((!I2C_IsStart(I2Cx)) && (I2C_HW_DEADLINE_TIMEOUT > timeout++));
    if (I2C_HW_DEADLINE_TIMEOUT <= timeout)
    {
        status |= I2C_HW_STATUS_START_TO_BUSY_TIMEOUT;
    }
    I2C_ClearStartFlag(I2Cx);

    /* wait core ready */
    timeout = 0;
    while ((!I2C_IsReady(I2Cx)) && (I2C_HW_DEADLINE_TIMEOUT > timeout++));
    if (I2C_HW_DEADLINE_TIMEOUT <= timeout)
    {
        status |= I2C_HW_STATUS_CORE_READY_TIMROUT;
    }

    return status;
}

/*!
* @brief I2C send stop
*
* @param[in] I2Cx: I2C module
*               - I2C0
* @return i2c hardware status
*/
uint32_t I2C_Stop(I2C_Type *I2Cx)
{
    uint32_t timeout = 0;
    uint32_t status = I2C_HW_STATUS_ERROR_NULL;

    DEVICE_ASSERT(I2C_INSTANCE_MAX > I2C_INDEX(I2Cx));

    I2C_TxEn(I2Cx);
    I2C_SendStop(I2Cx);

    /* wait idle status */
    while (I2C_IsBusy(I2Cx) && (I2C_HW_DEADLINE_TIMEOUT > timeout++));
    if (I2C_HW_DEADLINE_TIMEOUT <= timeout)
    {
        status |= I2C_HW_STATUS_STOP_TO_IDLE_TIMEOUT;
    }
    I2C_ClearStopFlag(I2Cx);

    /* wait core ready */
    timeout = 0;
    while ((!I2C_IsReady(I2Cx)) && (I2C_HW_DEADLINE_TIMEOUT > timeout++));
    if (I2C_HW_DEADLINE_TIMEOUT <= timeout)
    {
        status |= I2C_HW_STATUS_CORE_READY_TIMROUT;
    }

    return status;
}

/*!
* @brief wait one byte transmit finished
*
* @param[in] I2Cx: I2C module
*               - I2C0
* @return i2c hardware status
*/
uint32_t I2C_WaitOneByteFinished(I2C_Type *I2Cx)
{
    uint32_t timeout = 0, tmpReg = 0;
    uint32_t status = I2C_HW_STATUS_ERROR_NULL;

    tmpReg = I2C_GetStatus0(I2Cx);
    while ((!(tmpReg & I2C_STATUS0_BND_Msk)) && (I2C_HW_DEADLINE_TIMEOUT > timeout++))
    {
        tmpReg = I2C_GetStatus0(I2Cx);
    }
    if (tmpReg & I2C_STATUS0_RACK_Msk)
    {
        I2C_ClearStatus0(I2Cx, I2C_STATUS0_RACK_Msk);
        status |= I2C_HW_STATUS_NACK;
    }
    if (tmpReg & I2C_STATUS0_BND_Msk)
    {
        I2C_ClearStatus0(I2Cx, I2C_STATUS0_BND_Msk);
    }
    if (I2C_HW_DEADLINE_TIMEOUT <= timeout)
    {
        status |= I2C_HW_STATUS_BND_TIMEOUT;
    }

    return status;
}

/*!
* @brief I2C write one byte with polling BND flag
*        make sure to disable interrupt when use the interface
*
* @param[in] I2Cx: I2C module
*               - I2C0
* @param[in] data: data to write
* @return i2c hardware status
*/
uint32_t I2C_TransmitOneByte(I2C_Type *I2Cx, uint8_t data)
{
    uint32_t status = I2C_HW_STATUS_ERROR_NULL;

    DEVICE_ASSERT(I2C_INSTANCE_MAX > I2C_INDEX(I2Cx));

    I2C_TxEn(I2Cx);
    I2C_WriteDataReg(I2Cx, data);
    status = I2C_WaitOneByteFinished(I2Cx);

    return status;
}

/*!
* @brief I2C read one byte and send next clock
*        make sure to disable interrupt when use the interface
*
* @param[in] I2Cx: I2C module
*               - I2C0
* @param[in] data: data to read
* @param[in] nack: the next byte ack status
* @return i2c hardware status
*/
uint32_t I2C_ReceiveOneByte(I2C_Type *I2Cx, uint8_t *data, I2C_AckType nack)
{
    uint32_t timeout = 0;
    uint32_t status = I2C_HW_STATUS_ERROR_NULL;

    DEVICE_ASSERT(I2C_INSTANCE_MAX > I2C_INDEX(I2Cx));
    DEVICE_ASSERT(NULL != data);

    I2C_RxEn(I2Cx);
    if (I2C_ACK == nack)
    {
        I2C_SendAck(I2Cx);
    }
    else
    {
        I2C_SendNack(I2Cx);
    }
    *data = I2C_ReadDataReg(I2Cx);

    while ((!(I2C_GetStatus0(I2Cx) & I2C_STATUS0_BND_Msk)) && (I2C_HW_DEADLINE_TIMEOUT > timeout++));
    if (I2C_HW_DEADLINE_TIMEOUT <= timeout)
    {
        status |= I2C_HW_STATUS_BND_TIMEOUT;
    }

    return status;
}

/*!
* @brief I2C read one byte without send next clock
*
* @param[in] I2Cx: I2C module
*               - I2C0
* @param[in] data: data to read
* @return none
*/
void I2C_ReceiveLastOneByte(I2C_Type *I2Cx, uint8_t *data)
{
    DEVICE_ASSERT(I2C_INSTANCE_MAX > I2C_INDEX(I2Cx));

    /* change direction to avoid send out next byte clock */
    I2C_TxEn(I2Cx);

    *data = I2C_ReadDataReg(I2Cx);

    /* when direction is Tx, read data won't clear BND flag instead of write clear */
    I2C_ClearStatus0(I2Cx, I2C_STATUS0_BND_Msk);
}

/*!
* @brief I2C get internal hardware ready status
*
* @param[in] I2Cx: I2C module
*               - I2C0
* @return i2c hardware status
*/
uint32_t I2C_WaitForReady(I2C_Type *I2Cx)
{
    uint32_t timeout = 0;
    uint32_t status = I2C_HW_STATUS_ERROR_NULL;

    DEVICE_ASSERT(I2C_INSTANCE_MAX > I2C_INDEX(I2Cx));

    while ((!(I2C_GetStatus0(I2Cx) & I2C_STATUS0_READY_Msk)) && (I2C_HW_DEADLINE_TIMEOUT > timeout++));
    if (I2C_HW_DEADLINE_TIMEOUT <= timeout)
    {
        status |= I2C_HW_STATUS_CORE_READY_TIMROUT;
    }

    return status;
}

/*!
* @brief I2C burst write with 7bit/10bit addressing
*        make sure to disable interrupt when use the interface
*
* @param[in] I2Cx: I2C module
*               - I2C0
* @param[in] slaveAddr: slave 7bit/10bit address
* @param[in] buff: the data buffer
* @param[in] dataLength: the length to write
* @param[in] stopEn: enable/diable i2c send stop after lastone byte for a possible re-start
* @return i2c hardware status
*/
uint32_t I2C_MasterTransmitPoll(I2C_Type *I2Cx, uint16_t slaveAddr, uint8_t *buff, uint32_t dataLength, ACTION_Type stopEn)
{
    uint32_t i = 0;
    uint32_t status = I2C_HW_STATUS_ERROR_NULL;

    DEVICE_ASSERT(I2C_INSTANCE_MAX > I2C_INDEX(I2Cx));
    DEVICE_ASSERT(NULL != buff);

    status |= I2C_WaitForReady(I2Cx);
    status |= I2C_Start(I2Cx);
    if (!I2C_IsADEXT(I2Cx))
    {
        status |= I2C_TransmitOneByte(I2Cx, ((uint8_t)slaveAddr << 1) | (uint8_t)I2C_WRITE);
    }
    else
    {
        status |= I2C_TransmitOneByte(I2Cx, ((I2C_ADDEXT_PRIMARY_BYTE_FIX + (slaveAddr >> 8)) << 1) | (uint8_t)I2C_WRITE);
        status |= I2C_TransmitOneByte(I2Cx, (uint8_t)slaveAddr);
    }
    if (I2C_HW_STATUS_ERROR_NULL == status)
    {
        for (i = 0; i < dataLength; i++)
        {
            status |= I2C_TransmitOneByte(I2Cx, buff[i]);
            if (I2C_HW_STATUS_ERROR_NULL != status)
            {
                break;
            }
        }
        if (ENABLE == stopEn)
        {
            status |= I2C_Stop(I2Cx);
        }
    }
    else
    {
        status |= I2C_Stop(I2Cx);
    }

    return status;
}

/*!
* @brief I2C burst read with 7bit/10bit addressing
*        make sure to disable interrupt when use the interface
*
* @param[in] I2Cx: I2C module
*               - I2C0
* @param[in] slaveAddr: slave 7bit/10bit address
* @param[in] buff: the data buffer
* @param[in] dataLength: the length to read
* @return i2c hardware status
*/
uint32_t I2C_MasterReceivePoll(I2C_Type *I2Cx, uint16_t slaveAddr, uint8_t *buff, uint32_t dataLength)
{
    uint32_t i = 0;
    uint32_t status = I2C_HW_STATUS_ERROR_NULL;

    DEVICE_ASSERT(I2C_INSTANCE_MAX > I2C_INDEX(I2Cx));
    DEVICE_ASSERT(NULL != buff);
    DEVICE_ASSERT(0U < dataLength);

    status |= I2C_WaitForReady(I2Cx);
    status |= I2C_Start(I2Cx);
    if (!I2C_IsADEXT(I2Cx))
    {
        status |= I2C_TransmitOneByte(I2Cx, ((uint8_t)slaveAddr << 1) | (uint8_t)I2C_READ);
    }
    else
    {
        status |= I2C_TransmitOneByte(I2Cx, ((I2C_ADDEXT_PRIMARY_BYTE_FIX + (slaveAddr >> 8)) << 1) | (uint8_t)I2C_WRITE);
        status |= I2C_TransmitOneByte(I2Cx, (uint8_t)slaveAddr);
        status |= I2C_Restart(I2Cx);
        status |= I2C_TransmitOneByte(I2Cx, ((I2C_ADDEXT_PRIMARY_BYTE_FIX + (slaveAddr >> 8)) << 1) | (uint8_t)I2C_READ);
    }
    if (I2C_HW_STATUS_ERROR_NULL == status)
    {
        if (1 == dataLength)
        {
            status |= I2C_ReceiveOneByte(I2Cx, &buff[0], I2C_NACK);
            I2C_ReceiveLastOneByte(I2Cx, &buff[0]);
        }
        else
        {
            status |= I2C_ReceiveOneByte(I2Cx, &buff[0], I2C_ACK);
            for (i = 0; i < dataLength - 2; i++)
            {
                status |= I2C_ReceiveOneByte(I2Cx, &buff[i], I2C_ACK);
                if (I2C_HW_STATUS_ERROR_NULL != status)
                {
                    break;
                }
            }
            if (I2C_HW_STATUS_ERROR_NULL == status)
            {
                status |= I2C_ReceiveOneByte(I2Cx, &buff[i++], I2C_NACK);
                I2C_ReceiveLastOneByte(I2Cx, &buff[i]);
            }
        }
    }
    status |= I2C_Stop(I2Cx);

    return status;
}

#ifdef I2C_USE_INTERRUPT_TRANSMIT

/*!
* @brief I2C burst write with 7/10bit addressing by interrupt handler
*         related to variable: g_i2cTransmitInfo
*         related to function: I2C_MasterSlaveIntHandler
*         I2C interrupt must be enable
*         Stop will directly generate if master get an nack
*
* @param[in] I2Cx: I2C module
*               - I2C0
* @param[in] slaveAddr: slave 7/10bit address
* @param[in] buff: the data buffer
* @param[in] dataLength: the length to write
* @param[in] stopEn: enable/diable i2c send stop after lastone byte for a possible re-start
* @return i2c hardware status
*/
uint32_t I2C_MasterTransmitInt(I2C_Type *I2Cx, uint16_t slaveAddr, uint8_t *buff, uint32_t dataLength, ACTION_Type stopEn)
{
    uint8_t instance = I2C_INDEX(I2Cx);
    uint32_t status = I2C_HW_STATUS_ERROR_NULL;

    DEVICE_ASSERT(I2C_INSTANCE_MAX > instance);
    DEVICE_ASSERT(NULL != buff);

    status |= I2C_WaitForReady(I2Cx);
    status |= I2C_Start(I2Cx);
    if (I2C_HW_STATUS_ERROR_NULL == status)
    {
        g_i2cTransmitInfo[instance].pointer = 0;
        g_i2cTransmitInfo[instance].length = dataLength;
        g_i2cTransmitInfo[instance].buff = buff;
        g_i2cTransmitInfo[instance].stopEn = stopEn;
        g_i2cTransmitInfo[instance].wr = I2C_WRITE;
        g_i2cStatus[instance] = I2C_TRANSMIT_STATUS_ERROR_NULL;

        if (!I2C_IsADEXT(I2Cx))
        {
            I2C_TxEn(I2Cx);
            I2C_WriteDataReg(I2Cx, (((uint8_t)slaveAddr << 1) | (uint8_t)I2C_WRITE));
        }
        else
        {
            I2C_SetInterrupt(I2Cx, DISABLE);
            status |= I2C_TransmitOneByte(I2Cx, ((I2C_ADDEXT_PRIMARY_BYTE_FIX + (slaveAddr >> 8)) << 1) | (uint8_t)I2C_WRITE);
            I2C_SetInterrupt(I2Cx, ENABLE);
            if (I2C_HW_STATUS_ERROR_NULL == status)
            {
                I2C_TxEn(I2Cx);
                I2C_WriteDataReg(I2Cx, (uint8_t)slaveAddr);
            }
        }
    }

    return status;
}

/*!
* @brief I2C burst read with 7/10bit addressing by interrupt handler
*         related to variable: g_i2cTransmitInfo
*         related to function: I2C_MasterSlaveIntHandler
*         I2C interrupt must be enable
*
* @param[in] I2Cx: I2C module
*               - I2C0
* @param[in] slaveAddr: slave 7/10bit address
* @param[in] buff: the data buffer
* @param[in] dataLength: the length to read
* @return i2c hardware status
*/
uint32_t I2C_MasterReceiveInt(I2C_Type *I2Cx, uint16_t slaveAddr, uint8_t *buff, uint32_t dataLength)
{
    uint8_t instance = I2C_INDEX(I2Cx);
    uint32_t status = I2C_HW_STATUS_ERROR_NULL;

    DEVICE_ASSERT(I2C_INSTANCE_MAX > instance);
    DEVICE_ASSERT(NULL != buff);

    status |= I2C_WaitForReady(I2Cx);
    status |= I2C_Start(I2Cx);
    if (I2C_HW_STATUS_ERROR_NULL == status)
    {
        g_i2cTransmitInfo[instance].pointer = 0;
        g_i2cTransmitInfo[instance].length = dataLength;
        g_i2cTransmitInfo[instance].buff = buff;
        g_i2cTransmitInfo[instance].wr = I2C_READ;
        g_i2cStatus[instance] = I2C_TRANSMIT_STATUS_ERROR_NULL;

        if (!I2C_IsADEXT(I2Cx))
        {
            I2C_TxEn(I2Cx);
            I2C_WriteDataReg(I2Cx, (uint8_t)((slaveAddr << 1) | (uint8_t)I2C_READ));
        }
        else
        {
            I2C_SetInterrupt(I2Cx, DISABLE);
            status |= I2C_TransmitOneByte(I2Cx, ((I2C_ADDEXT_PRIMARY_BYTE_FIX + (slaveAddr >> 8)) << 1) | (uint8_t)I2C_WRITE);
            status |= I2C_TransmitOneByte(I2Cx, (uint8_t)slaveAddr);
            status |= I2C_Restart(I2Cx);
            I2C_SetInterrupt(I2Cx, ENABLE);
            if (I2C_HW_STATUS_ERROR_NULL == status)
            {
                I2C_TxEn(I2Cx);
                I2C_WriteDataReg(I2Cx, ((I2C_ADDEXT_PRIMARY_BYTE_FIX + (slaveAddr >> 8)) << 1) | (uint8_t)I2C_READ);
            }
        }
    }

    return status;
}

/*!
* @brief I2C slave burst write/read by interrupt handler
*         related to variable: g_i2cTransmitInfo
*         related to function: I2C_MasterSlaveIntHandler
*         I2C interrupt must be enable
*
* @param[in] I2Cx: I2C module
*               - I2C0
* @param[in] buff: the data buffer
* @param[in] dataLength: the length to write/read
* @return none
*/
void I2C_SlaveTransmitReceiveInt(I2C_Type *I2Cx, uint8_t *buff, uint32_t dataLength)
{
    uint8_t instance = I2C_INDEX(I2Cx);

    DEVICE_ASSERT(I2C_INSTANCE_MAX > instance);
    DEVICE_ASSERT(NULL != buff);
    DEVICE_ASSERT(dataLength > 0U);

    g_i2cTransmitInfo[instance].pointer = 0;
    g_i2cTransmitInfo[instance].length = dataLength;
    g_i2cTransmitInfo[instance].buff = buff;

    g_i2cStatus[instance] = I2C_TRANSMIT_STATUS_ERROR_NULL;
}

/*!
* @brief get transmit status when use
*        I2C_MasterTransmitInt, I2C_MasterReceiveInt, I2C_SlaveTransmitReceiveInt
*
* @param[in] I2Cx: I2C module
*               - I2C0
* @return i2c application status
*/
uint32_t I2C_GetTransmitReceiveStatus(I2C_Type *I2Cx)
{
    uint8_t instance = I2C_INDEX(I2Cx);

    DEVICE_ASSERT(I2C_INSTANCE_MAX > instance);

    return g_i2cStatus[instance];
}

/*!
* @brief reset transmit status to I2C_TRANSMIT_STATUS_ERROR_NULL before using
*        I2C_MasterTransmitInt, I2C_MasterReceiveInt, I2C_SlaveTransmitReceiveInt
*
* @param[in] I2Cx: I2C module
*               - I2C0
* @return none
*/
void I2C_ResetTransmitReceiveStatus(I2C_Type *I2Cx)
{
    uint8_t instance = I2C_INDEX(I2Cx);

    DEVICE_ASSERT(I2C_INSTANCE_MAX > instance);

    g_i2cTransmitInfo[instance].pointer = 0;
    g_i2cTransmitInfo[instance].length = 0;
    g_i2cTransmitInfo[instance].buff = NULL;

    g_i2cStatus[instance] = I2C_TRANSMIT_STATUS_ERROR_NULL;
}

/*!
* @brief get transmitted length when use I2C_MasterTransmitInt, I2C_MasterReceiveInt, I2C_SlaveTransmitReceiveInt
*
* @param[in] I2Cx: I2C module
*               - I2C0
* @return transmitted length
*/
uint32_t I2C_GetTransmittedReceivedLength(I2C_Type *I2Cx)
{
    uint8_t instance = I2C_INDEX(I2Cx);

    DEVICE_ASSERT(I2C_INSTANCE_MAX > instance);

    return g_i2cTransmitInfo[instance].pointer;
}

/*!
* @brief Master interrupt handler routine when use
*        I2C_MasterTransmitInt, I2C_MasterReceiveInt
*
* @param[in] I2Cx: I2C module
*               - I2C0
* @param[in] wparam: function parameter
* @param[in] lparam: function parameter
* @return none
*/
static void I2C_MasterIntHandler(I2C_Type *I2Cx, uint32_t wparam, uint32_t lparam)
{
    uint8_t instance = I2C_INDEX(I2Cx);

    DEVICE_ASSERT(I2C_INSTANCE_MAX > instance);

    if (I2C_IsARBLost(I2Cx))
    {
        I2C_ClearStatus0(I2Cx, I2C_STATUS0_ARBLOST_Msk);
        I2C_ClearStatus0(I2Cx, I2C_STATUS0_BND_Msk);
        g_i2cStatus[instance] |= I2C_TRANSMIT_STATUS_ARBLOST;
        g_i2cStatus[instance] |= I2C_TRANSMIT_STATUS_FINISHED;
    }
    else
    {
        if (I2C_IsTx(I2Cx))
        {
            if (0 == g_i2cTransmitInfo[instance].pointer)
            {
                if (wparam & I2C_STATUS0_RACK_Msk)
                {
                    g_i2cStatus[instance] |= I2C_TRANSMIT_STATUS_NOT_MATCH;
                    g_i2cStatus[instance] |= I2C_TRANSMIT_STATUS_NACK;
                    g_i2cStatus[instance] |= I2C_TRANSMIT_STATUS_FINISHED;
                    I2C_ClearStatus0(I2Cx, I2C_STATUS0_BND_Msk);
                    I2C_ClearStatus0(I2Cx, I2C_STATUS0_RACK_Msk);
                    I2C_SendStop(I2Cx);
                }
                else
                {
                    if (0 < g_i2cTransmitInfo[instance].length)
                    {
                        if (I2C_WRITE == g_i2cTransmitInfo[instance].wr)
                        {
                            I2C_WriteDataReg(I2Cx, g_i2cTransmitInfo[instance].buff[g_i2cTransmitInfo[instance].pointer++]);
                        }
                        else
                        {
                            I2C_RxEn(I2Cx);
                            if (1 == g_i2cTransmitInfo[instance].length)
                            {
                                I2C_SendNack(I2Cx);
                            }
                            else
                            {
                                I2C_SendAck(I2Cx);
                            }
                            I2C_DumpReadDataReg(I2Cx);
                        }
                    }
                    else
                    {
                        g_i2cStatus[instance] |= I2C_TRANSMIT_STATUS_MATCHED;
                        g_i2cStatus[instance] |= I2C_TRANSMIT_STATUS_FINISHED;
                        I2C_ClearStatus0(I2Cx, I2C_STATUS0_BND_Msk);
                        if (ENABLE == g_i2cTransmitInfo[instance].stopEn)
                        {
                            I2C_SendStop(I2Cx);
                        }
                    }
                }
            }
            else if (g_i2cTransmitInfo[instance].length > g_i2cTransmitInfo[instance].pointer)
            {
                if (wparam & I2C_STATUS0_RACK_Msk)
                {
                    g_i2cStatus[instance] |= I2C_TRANSMIT_STATUS_NACK;
                    g_i2cStatus[instance] |= I2C_TRANSMIT_STATUS_FINISHED;
                    I2C_ClearStatus0(I2Cx, I2C_STATUS0_BND_Msk);
                    I2C_ClearStatus0(I2Cx, I2C_STATUS0_RACK_Msk);
                    I2C_SendStop(I2Cx);
                }
                else
                {
                    I2C_WriteDataReg(I2Cx, g_i2cTransmitInfo[instance].buff[g_i2cTransmitInfo[instance].pointer++]);
                }
            }
            else
            {
                if (wparam & I2C_STATUS0_RACK_Msk)
                {
                    g_i2cStatus[instance] |= I2C_TRANSMIT_STATUS_NACK;
                    I2C_ClearStatus0(I2Cx, I2C_STATUS0_RACK_Msk);
                }
                g_i2cStatus[instance] |= I2C_TRANSMIT_STATUS_FINISHED;
                I2C_ClearStatus0(I2Cx, I2C_STATUS0_BND_Msk);

                if (ENABLE == g_i2cTransmitInfo[instance].stopEn)
                {
                    I2C_SendStop(I2Cx);
                }
            }
        }
        else
        {
            if (1 == g_i2cTransmitInfo[instance].length)
            {
                g_i2cStatus[instance] |= I2C_TRANSMIT_STATUS_FINISHED;
                I2C_ReceiveLastOneByte(I2Cx, &g_i2cTransmitInfo[instance].buff[g_i2cTransmitInfo[instance].pointer++]);
                I2C_SendAck(I2Cx);
                I2C_SendStop(I2Cx);
            }
            else if ((g_i2cTransmitInfo[instance].length - 2) > g_i2cTransmitInfo[instance].pointer)
            {
                I2C_SendAck(I2Cx);
                g_i2cTransmitInfo[instance].buff[g_i2cTransmitInfo[instance].pointer++] = I2C_ReadDataReg(I2Cx);
            }
            else if ((g_i2cTransmitInfo[instance].length - 2) == g_i2cTransmitInfo[instance].pointer)
            {
                I2C_SendNack(I2Cx);
                g_i2cTransmitInfo[instance].buff[g_i2cTransmitInfo[instance].pointer++] = I2C_ReadDataReg(I2Cx);
            }
            else
            {
                g_i2cStatus[instance] |= I2C_TRANSMIT_STATUS_FINISHED;
                I2C_ReceiveLastOneByte(I2Cx, &g_i2cTransmitInfo[instance].buff[g_i2cTransmitInfo[instance].pointer++]);
                I2C_SendAck(I2Cx);
                I2C_SendStop(I2Cx);
            }
        }
    }
}

/*!
* @brief Slave interrupt handler routine when use I2C_SlaveWriteReceiveInt
*        slave BND flag is trig only address match
*        master nack will not trig BND interrupt anymore
*        slave nack will not trig BND interrupt anymore
*
* @param[in] I2Cx: I2C module
*               - I2C0
* @param[in] wparam: function parameter
* @param[in] lparam: function parameter
* @return none
*/
static void I2C_SlaveIntHandler(I2C_Type *I2Cx, uint32_t wparam, uint32_t lparam)
{
    uint8_t instance = I2C_INDEX(I2Cx);

    DEVICE_ASSERT(I2C_INSTANCE_MAX > instance);

    if (wparam & I2C_STATUS0_SAMF_Msk)
    {
        I2C_ClearStatus0(I2Cx, I2C_STATUS0_SAMF_Msk);
        g_i2cStatus[instance] |= I2C_TRANSMIT_STATUS_MATCHED;

        if (wparam & I2C_STATUS0_SRW_Msk)
        {
            if (g_i2cTransmitInfo[instance].length > 0)
            {
                I2C_WriteDataReg(I2Cx, g_i2cTransmitInfo[instance].buff[g_i2cTransmitInfo[instance].pointer]);
            }
        }
        else
        {
            if (g_i2cTransmitInfo[instance].length > 1)
            {
                I2C_SendAck(I2Cx);
            }
            else if (g_i2cTransmitInfo[instance].length == 1)
            {
                I2C_SendNack(I2Cx);
            }
            else
            {
                I2C_SendNack(I2Cx);
                g_i2cStatus[instance] |= I2C_TRANSMIT_STATUS_FINISHED;
            }
        }
    }
    else
    {
        if (wparam & I2C_STATUS0_SRW_Msk)
        {
            if (g_i2cTransmitInfo[instance].length - 1 >= g_i2cTransmitInfo[instance].pointer)
            {
                g_i2cTransmitInfo[instance].pointer++;
            }
            if (wparam & I2C_STATUS0_RACK_Msk)
            {
                if (!I2C_IsTxUF(I2Cx))
                {
                    g_i2cStatus[instance] |= I2C_TRANSMIT_STATUS_NACK;
                    g_i2cStatus[instance] |= I2C_TRANSMIT_STATUS_FINISHED;
                }
                I2C_ClearStatus0(I2Cx, I2C_STATUS0_BND_Msk);
                I2C_ClearStatus0(I2Cx, I2C_STATUS0_RACK_Msk);
            }
            else
            {
                if (g_i2cTransmitInfo[instance].length > g_i2cTransmitInfo[instance].pointer)
                {
                    I2C_WriteDataReg(I2Cx, g_i2cTransmitInfo[instance].buff[g_i2cTransmitInfo[instance].pointer]);
                }
                else
                {
                    if (I2C_IsStretch(I2Cx))
                    {
                        I2C_SetStretch(I2Cx, DISABLE);
                    }
                    if (!I2C_IsTxUF(I2Cx))
                    {
                        g_i2cStatus[instance] |= I2C_TRANSMIT_STATUS_FINISHED;
                    }
                    I2C_ClearStatus0(I2Cx, I2C_STATUS0_BND_Msk);
                }
            }
            if (I2C_IsTxUF(I2Cx))
            {
                g_i2cStatus[instance] |= I2C_TRANSMIT_STATUS_TXUF;
                I2C_ClearStatus1(I2Cx, I2C_STATUS1_TXUF_Msk);
            }
        }
        else
        {
            if (g_i2cTransmitInfo[instance].length > g_i2cTransmitInfo[instance].pointer)
            {
                g_i2cTransmitInfo[instance].buff[g_i2cTransmitInfo[instance].pointer++] = I2C_ReadDataReg(I2Cx);
                if (g_i2cTransmitInfo[instance].length - 1 == g_i2cTransmitInfo[instance].pointer)
                {
                    I2C_SendNack(I2Cx);
                }
                else if (g_i2cTransmitInfo[instance].length == g_i2cTransmitInfo[instance].pointer)
                {
                    g_i2cStatus[instance] |= I2C_TRANSMIT_STATUS_FINISHED;

                }
            }
            else
            {
                I2C_ClearStatus0(I2Cx, I2C_STATUS0_BND_Msk);
            }
        }
    }
}

/*!
* @brief Master/Slave interrupt handler routine when use
*        I2C_MasterTransmitInt, I2C_MasterReceiveInt, I2C_SlaveTransmitReceiveInt
*
* @param[in] I2Cx: I2C module
*               - I2C0
* @param[in] wparam: function parameter
* @param[in] lparam: function parameter
* @return none
*/
static void I2C_MasterSlaveIntHandler(void *device, uint32_t wparam, uint32_t lparam)
{
    I2C_Type *I2Cx = (I2C_Type *)device;
    uint8_t instance = I2C_INDEX(I2Cx);

    DEVICE_ASSERT(I2C_INSTANCE_MAX > instance);

    if (I2C_IsStart(I2Cx) && I2C_IsSSIntEnable(I2Cx))
    {
        I2C_ClearStartFlag(I2Cx);
        g_i2cStatus[instance] |= I2C_TRANSMIT_STATUS_START_FLAG;
    }
    else if (I2C_IsStop(I2Cx) && I2C_IsSSIntEnable(I2Cx))
    {
        I2C_ClearStopFlag(I2Cx);
        g_i2cStatus[instance] |= I2C_TRANSMIT_STATUS_STOP_FLAG;
        g_i2cStatus[instance] |= I2C_TRANSMIT_STATUS_FINISHED;
    }
    else
    {
        if (I2C_IsMaster(I2Cx))
        {
            I2C_MasterIntHandler(I2Cx, wparam, lparam);
        }
        else
        {
            I2C_SlaveIntHandler(I2Cx, wparam, lparam);
        }
    }
}

#endif /* I2C_USE_INTERRUPT_TRANSMIT */

/* =============================================  EOF  ============================================== */
