#include "gw3323_iic.h"

/**
  * @brief  Initializes the IIC peripheral according to the specified
  *         parameters in the iic_init_typedef.
  * @param  iic_init_struct: pointer to a iic_init_typedef structure that
  *         contains the configuration information for the specified IIC peripheral.
  * @retval None
  */
void iic_init(iic_init_typedef *iic_init_struct)
{
    // Configure the iic clock source.
    CLKCON1 &= ~(1 << 23);
    CLKCON1 |= iic_init_struct->clk_source;

    // Configure the iic scl pose div conter.
    IIC->con0 &= ~IIC_CON0_POSDIV;
    if(iic_init_struct->scl_pose_div>63)
    {
        IIC->con0 |= ((63 ) << 4) & IIC_CON0_POSDIV;
    }
    else
    {
        IIC->con0 |= ((iic_init_struct->scl_pose_div ) << 4) & IIC_CON0_POSDIV;
    }
    // Configure the iic sda hold cnt when scl falling.
    IIC->con0 &= ~IIC_CON0_HOLDCNT;
    IIC->con0 |= ((iic_init_struct->sda_hold_cnt ) << 2) & IIC_CON0_HOLDCNT;

    RSTCON0 |= (1 << 3);
}

/**
  * @brief  Enable or disable the specified IIC peripheral.
  * @param  state: state of the IIC peripheral.
  *         This parameter can be: ENABLE or DISABLE.
  * @retval None
  */
AT(.com_text.iic)
void iic_cmd(FUNCTIONAL_STATE state)
{
    if (state != DISABLE) {
        IIC->con0 |= IIC_CON0_IICEN;
        IIC->con0 |= IIC_CON0_INTEN;
    } else {
        IIC->con0 &= ~IIC_CON0_IICEN;
        IIC->con0 &= ~IIC_CON0_INTEN;
    }
}

/**
  * @brief  Initializes the IIC control regisger according to param.
  * @param  transmit_mode: select the iic transmit direction.
  *         This parameter can be: IIC_SEND_MODE or IIC_RECEIVE_MODE.
  * @param  dev_addr0: the slave device address 0.
  * @param  reg_addr: the address of the slave device.
  * @param  len: the numbers of bytes of data.
  * @param  dev_addr1: the slave device address 1.
  * @retval None
  */
void iic_transmit_config(uint8_t transmit_mode, uint8_t dev_addr0, uint16_t reg_addr, uint8_t len, uint8_t dev_addr1)
{
    IIC->con1 = 0;
    IIC->con1 |= IIC_CON1_STOP_EN;

    IIC->con1 |= IIC_CON1_ADR0_EN;
    IIC->con1 |= IIC_CON1_CTL0_EN;
    IIC->con1 |= IIC_CON1_START0_EN;
    IIC->con1 |= len & IIC_CON1_DATA_CNT;
    if (reg_addr & 0xff00) {
        IIC->con1 |= IIC_CON1_ADR1_EN;
    }

    IIC->cmda = dev_addr0 | (reg_addr << 8);

    if (transmit_mode == IIC_SEND_MODE) {
        IIC->con1 |= IIC_CON1_WDAT_EN;
    } else if (transmit_mode == IIC_RECEIVE_MODE) {
        IIC->con1 |= IIC_CON1_TXNAK_EN;
        IIC->con1 |= IIC_CON1_RDAT_EN;
        IIC->con1 |= IIC_CON1_CTL1_EN;
        IIC->con1 |= IIC_CON1_START1_EN;
        IIC->cmda |= (dev_addr1 << 24);
    }
}

/**
  * @brief  Start a iic transmit, that should be called after the register is configured.
  * @param  None
  * @retval None
  */
AT(.com_text.iic)
void iic_start_transmit(void)
{
    IIC->con0 |= IIC_CON0_KS;
}

/**
  * @brief  Send data by iic bus, this function will block program.
  * @param  dev_addr: the slave device address.
  * @param  reg_addr: the address of the slave device.
  * @param  data: the data needed to transmit.
  * @param  len: the numbers of bytes of data.
  * @retval None
  */
void iic_send_data(uint8_t dev_addr, uint16_t reg_addr, uint32_t data, uint8_t len)
{
    iic_transmit_config(IIC_SEND_MODE, dev_addr, reg_addr, len, 0);

    IIC->data = data;

    IIC->con0 |= IIC_CON0_KS;
    while ((IIC->con0 & IIC_CON0_DONE) == RESET);
    IIC->con0 |= IIC_CON0_CLR_DONE;
}

/**
  * @brief  Receive data by iic bus, this function will block program.
  * @param  dev_addr0: the slave device address 0.
  * @param  reg_addr: the address of the slave device.
  * @param  len: the numbers of bytes of data.
  * @param  dev_addr1: the slave device address 1.
  * @retval None
  */
uint32_t iic_receive_data(uint8_t dev_addr0, uint16_t reg_addr, uint8_t len, uint8_t dev_addr1)
{
    iic_transmit_config(IIC_RECEIVE_MODE, dev_addr0, reg_addr, len, dev_addr1);

    IIC->con0 |= IIC_CON0_KS;
    while ((IIC->con0 & IIC_CON0_DONE) == RESET);
    IIC->con0 |= IIC_CON0_CLR_DONE;
    return IIC->data;
}

/**
  * @brief  Enable or disable the specified IIC interrupt.
  * @param  isr: Function to be executed for service interruption.
  * @param  pr: Priority of service interruption.
  * @param  iic_ie_bit: specifies the IIC interrupt sources to be enable or disable.
  *         this parameter can be one of the following values:
  *             @arg IIC_IT_DONE: IIC transmit finished interrupt.
  * @param  state: the state of the IIC peripheral.
  *         this parameter can be: ENABLE or DISABLE.
  * @retval None
  */
void iic_pic_config(isr_t isr, int pr, uint16_t iic_ie_bit, FUNCTIONAL_STATE state)
{
    if (state != DISABLE) {
        IIC->con0 |= iic_ie_bit;
        sys_irq_init(IIC_BSP_IRQn, pr, isr);
    } else {
        PICEN &= ~BIT(IIC_BSP_IRQn);
        IIC->con0 &= ~(uint32_t)iic_ie_bit;
    }
}

/**
  * @brief  Get the IIC interrupt pending.
  * @param  iic_flag: specifies the flag to get.
  *         this parameter can be one of the following values:
  *             @arg IIC_FLAG_DONE: IIC transmit finished flag.
  *             @arg IIC_FLAG_ACK: IIC receive slave device ack flag.
  * @retval The state of iic_flag (SET or RESET).
  */
AT(.com_text.iic)
FLAG_STATE iic_get_flag(uint32_t iic_flag)
{
    if ((IIC->con0 & iic_flag) != RESET) {
        return SET;
    } else {
        return RESET;
    }
}

/**
  * @brief  Clear the IIC interrupt pending.
  * @param  iic_flag: specifies the flag to clear.
  *         this parameter can be one of the following values:
  *             @arg IIC_FLAG_DONE: IIC transmit finished flag.
  * @retval The state of iic_flag (SET or RESET).
  */
AT(.com_text.iic)
void iic_clear_flag(uint32_t iic_flag)
{
    IIC->con0 |= (uint32_t)iic_flag;
}
