/**************************************************************************//**
 * @file     pan_i2c.c
 * @version  V1.00
 * $Revision: 2 $
 * $Date: 16/02/2216:24 $
 * @brief    Panchip series I2C driver source file
 *
 * @note
 * Copyright (C) 2025 Panchip Technology Corp. All rights reserved.
*****************************************************************************/
#include "pan271x.h"
#include "pan_i2c.h"
#include "pan_clk.h"

/** @defgroup I2C
 * @brief I2C driver modules
 * @{
 */

/** @defgroup I2C_Private_Functions
 * @{
 */

/**
 * @brief  Initializes the I2C peripheral according to the specified parameters .
 * @param  Division: I2C clock division
 *         @arg: I2C_DIV_256
 *         @arg: I2C_DIV_224
 *         @arg: I2C_DIV_192
 *         @arg: I2C_DIV_160
 *         @arg: I2C_DIV_960
 *         @arg: I2C_DIV_120
 *         @arg: I2C_DIV_60
 * @retval None
 */
void I2C_Init(I2C_Division_t Division)
{
	I2C_SetDivision(Division);
    I2C->CON |= IIC_CON_IIC_EN_Msk;
}

/**
 * @brief  Denitializes the I2C peripheral.
 * @param  None
 * @retval None
 */
void I2C_DeInit(void)
{
    I2C->CON = 0x00;
}

/**
 * @brief       Get current iic division
 * @return      division value
 */
uint16_t I2C_GetDivision(void)
{
    switch(I2C->CON & I2C_DIV_MSK) {
		case I2C_DIV_256 : return 256;
		case I2C_DIV_224 : return 224;
		case I2C_DIV_192 : return 192;
		case I2C_DIV_160 : return 160;
		case I2C_DIV_960 : return 960;
		case I2C_DIV_120 : return 120;
		case I2C_DIV_60  : return 60;
		default: return 0;
	}
}



/**
 * @brief I2C Start Function
 * @details This function initiates the I2C communication 
 *           by generating a start condition on the bus.
 * 
 * @retval TRUE if the start condition was successfully generated, 
 *          FALSE otherwise.
 */
uint8_t I2C_Start(void)
{
    uint8_t ret;

    I2C->CON &= ~IIC_CON_INT_FLAG_Msk;
    I2C->CON |= IIC_CON_START_FLAG_Msk;
    ret = I2C_GetStaus(STARTED, I2C_TIMEOUT);
    if (ret != TRUE)
    {
        I2C->CON |= IIC_CON_STOP_FLAG_Msk;
        goto exit;
    }

    I2C->CON &= ~IIC_CON_START_FLAG_Msk; // Clear IT flag

exit:
    return ret;
}

/**
 * @brief Generates a restart condition on the I2C bus.
 * @param None
 * @retval TRUE if the start condition was successfully generated, 
 *          FALSE otherwise.
 */
uint8_t I2C_Restart(void)
{
    uint8_t ret;

    I2C->CON |= IIC_CON_START_FLAG_Msk;
    I2C->CON &= ~IIC_CON_INT_FLAG_Msk;
    ret = I2C_GetStaus(REPEAT_STARTED, I2C_TIMEOUT);
    if (ret != TRUE)
    {
        I2C->CON |= IIC_CON_STOP_FLAG_Msk;
        goto exit;
    }

    I2C->CON &= ~IIC_CON_START_FLAG_Msk; // Clear IT flag
    
exit:
    return ret;
}

/**
* @brief  Send stop by I2C .
* @param  None
* @retval None
*/
void I2C_Stop(void)
{
    I2C->CON |= IIC_CON_STOP_FLAG_Msk;
    I2C->CON &= ~IIC_CON_INT_FLAG_Msk;
}

/**
 * @brief Sends the device address during I2C communication.
 * @param Addr The device read or write address to be sent.
 * @param Op The operation type (I2C_Op_t) indicating 
 *            whether it is a read or write operation.
 * @return TRUE if the device address was successfully sent, 
 *         FALSE otherwise.
 */
uint8_t I2C_SendDevAddr( uint8_t Addr, I2C_Op_t Op)
{
    uint8_t ret;

    I2C->DATACMD = Addr;
    I2C->CON &= ~IIC_CON_INT_FLAG_Msk;    // Clear interrupt flag
    if(Op == I2C_WRITE)    // Write operation
    {
        ret = I2C_GetStaus(WR_ADDR_ACK, I2C_TIMEOUT);
        if (ret != TRUE)
        {
            I2C->CON |= IIC_CON_STOP_FLAG_Msk;
            goto exit;
        }        
    }
    else    // Read operation
    {
        ret = I2C_GetStaus(RD_ADDR_ACK, I2C_TIMEOUT);
        if (ret != TRUE)
        {
            I2C->CON |= IIC_CON_STOP_FLAG_Msk;
            goto exit;
        }

        I2C->CON &= ~IIC_CON_INT_FLAG_Msk; // Clear interrupt flag
    }
    
exit:
    return ret;
}

/**
 * @brief Writes a byte of data during I2C communication.
 * 
 * @param Data The byte of data to be transmitted.
 * @return TRUE if the byte of data was successfully transmitted, 
 *         FALSE otherwise.
 */
uint8_t I2C_WriteByte(uint8_t Data)
{
    uint8_t ret;

    I2C->DATACMD = Data;
    I2C->CON &= ~IIC_CON_INT_FLAG_Msk; // Clear interrupt flag
    ret = I2C_GetStaus(WR_DATA_ACK, I2C_TIMEOUT);
    return ret;
}

/**
 * @brief Reads a byte of data during I2C communication.
 * 
 * @param pData Pointer to the memory location where the received data will be stored.
 * @param Ack The type of acknowledgment (I2C_TypeAck_t) to be sent after receiving the byte.
 * @return TRUE if the byte of data was successfully transmitted, 
 *         FALSE otherwise.
 */
uint8_t I2C_ReadByte(uint8_t *pData, I2C_TypeAck_t Ack)
{
    uint8_t ret;
    I2C_Status_t status;

    if (Ack == I2C_TYPE_ACK)
    {
        status = RD_DATA_ACK;
    }
    else
    {
        status = RD_DATA_NACK;
    }
    
    ret = I2C_GetStaus(status, I2C_TIMEOUT);
    if (ret != TRUE)
    {
        goto exit;
    }

    *pData = I2C->DATACMD;
    
    if (Ack == I2C_TYPE_ACK)
    {
        I2C->CON &= ~IIC_CON_INT_FLAG_Msk;
    }

exit:
    return ret;
}

/**
 * @brief  Enables the acknowledgment (ACK) signal during I2C communication.
 * @param  None
 * @retval None
 */
void I2C_EnableAck(void)
{
    I2C->CON |= IIC_CON_ACK_EN_Msk; // Should ack to slave
}

/**
 * @brief  Disables the acknowledgment (ACK) signal during I2C communication.
 * @param  None
 * @retval None
 */
void I2C_DisableAck(void)
{
    I2C->CON &= ~IIC_CON_ACK_EN_Msk; // Should ack to slave
}


/**
 * @brief  Get I2C bus status value
 * @param  None
 * @retval TRUE if the status is right, 
 *          FALSE otherwise.
 */
bool I2C_GetStaus(I2C_Status_t status, uint16_t timeout)
{
    while (timeout--)
    {
        if ((I2C->CON & IIC_CON_INT_FLAG_Msk) && (I2C->STATUS == status))
        {
            return TRUE;
        }
    }

    return FALSE;
}

/**
 * @}
 */

/**
 * @}
 */

#ifndef __OS_SYSTEM_MODE__
void I2C_OsRead(I2C_T* I2Cx,uint8_t *ReadBuf,uint32_t Size)
{
}
#endif
