/* Includes ------------------------------------------------------------------*/
#include "drv_eeprom_24c02.h"
#include "FE82160_i2c.h"

/* Private typedef -----------------------------------------------------------*/
typedef enum{
  I2C_DATA_Write,
  I2C_DATA_Read,
  I2C_DATA_Probe,
} I2C_DATA_DIRECTION;

typedef enum {
  I2C_MASTATUS_IDLE,
  I2C_MASTATUS_OK,
  I2C_MASTATUS_ERROR,
  I2C_MASTATUS_NOACK
} I2C_MasterStatus_TypeDef;

/* Private define ------------------------------------------------------------*/
#define I2C_CR_SET(Flag)  (I2C->CR = (I2C->CR | Flag))
#define I2C_CR_CLR(Flag)  (I2C->CR = ((I2C->CR & ~(Flag))))
#define I2C_CR_FLAG (I2C_CR_STA | I2C_CR_STO | I2C_CR_SI | I2C_CR_AA)

#define I2C_SPEED_RATE 100
#define I2C_DEV_ADDR   0xA0

#define I2C_STAT_ERROR_UNDER          ((uint32_t)0x00000000U)
#define I2C_STAT_USELESS              ((uint32_t)0x000000F8U)

#define I2C_STAT_MASTER_TX_START      ((uint32_t)0x00000008U)     /*!< A start condition has been transmitted */
#define I2C_STAT_MASTER_TX_RESTART    ((uint32_t)0x00000010U)     /*!< A repeat start condition has been transmitted */
#define I2C_STAT_MASTER_TX_SLAW_ACK   ((uint32_t)0x00000018U)     /*!< SLA+W has been transmitted, ACK has been received */
#define I2C_STAT_MASTER_TX_SLAW_NOACK ((uint32_t)0x00000020U)     /*!< SLA+W has been transmitted, NACK has been received */
#define I2C_STAT_MASTER_TX_DATA_ACK   ((uint32_t)0x00000028U)     /*!< Data has been transmitted, ACK has been received */
#define I2C_STAT_MASTER_TX_DATA_NOACK ((uint32_t)0x00000030U)     /*!< Data has been transmitted, NACK has been received */
#define I2C_STAT_MASTER_TX_LOST_SCL   ((uint32_t)0x00000038U)

#define I2C_STAT_MASTER_RX_START      ((uint32_t)0x00000008U)     /*!< A start condition has been transmitted */
#define I2C_STAT_MASTER_RX_RESTART    ((uint32_t)0x00000010U)     /*!< A repeat start condition has been transmitted */
#define I2C_STAT_MASTER_RX_SLAR_ACK   ((uint32_t)0x00000040U)     /*!< SLA+R has been transmitted, ACK has been received */
#define I2C_STAT_MASTER_RX_SLAW_NOACK ((uint32_t)0x00000048U)     /*!< SLA+R has been transmitted, NACK has been received */
#define I2C_STAT_MASTER_RX_DATA_ACK   ((uint32_t)0x00000050U)     /*!< Data has been received, ACK has been returned */
#define I2C_STAT_MASTER_RX_DATA_NOACK ((uint32_t)0x00000058U)     /*!< Data has been received, NACK has been returned */
#define I2C_STAT_MASTER_RX_LOST_SCL   ((uint32_t)0x00000038U)

#define I2C_STAT_SLAVE_TX_SLAW_ACK    ((uint32_t)0x000000A8U)
#define I2C_STAT_SLAVE_TX_DATA_ACK    ((uint32_t)0x000000B8U)
#define I2C_STAT_SLAVE_TX_DATA_NOACK  ((uint32_t)0x000000C0U)
#define I2C_STAT_SLAVE_TX_DATA_LAST   ((uint32_t)0x000000C8U)
#define I2C_STAT_SLAVE_TX_LOST_SCL    ((uint32_t)0x000000B0U)

#define I2C_STAT_SLAVE_RX_SLAW_ACK    ((uint32_t)0x00000060U)
#define I2C_STAT_SLAVE_RX_BROAD_ACK   ((uint32_t)0x00000070U)
#define I2C_STAT_SLAVE_RX_SDATA_ACK   ((uint32_t)0x00000080U)
#define I2C_STAT_SLAVE_RX_SDATA_NOACK ((uint32_t)0x00000088U)
#define I2C_STAT_SLAVE_RX_BDATA_ACK   ((uint32_t)0x00000090U)
#define I2C_STAT_SLAVE_RX_BDATA_NOACK ((uint32_t)0x00000098U)
#define I2C_STAT_SLAVE_RX_SA_LOST_SCL ((uint32_t)0x00000068U)
#define I2C_STAT_SLAVE_RX_BA_LOST_SCL ((uint32_t)0x00000078U)
#define I2C_STAT_SLAVE_STOP_RESTART   ((uint32_t)0x000000A0U)

/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
static I2C_MasterStatus_TypeDef masterStatus = I2C_MASTATUS_IDLE;
static I2C_DATA_DIRECTION dataDirection;
static uint8_t devAddress = 0, regAddress;
static uint8_t *pTxBuf = 0, *pRxBuf = 0;
static int txSize = 0, rxSize = 0;

/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/

void eeprom_24c02_init(void)
{
  I2C_InitTypeDef I2C_InitStruct;

  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOC, ENABLE);
  RCC_APBPeriphClockCmd(RCC_APBPeriph_I2C, ENABLE);

  I2C_DeInit();

  /*
    PC3 (I2C_SDA)
    PC4 (I2C_SCL)
  */
  GPIO_Init(GPIOC, GPIO_Pin_3 | GPIO_Pin_4, GPIO_SPEED_LOW | GPIO_PUPD_UP | GPIO_OTYPE_OD | GPIO_DRV_LOW);
  GPIO_SetAFPin(GPIOC, GPIO_Pin_3 | GPIO_Pin_4, GPIO_AF4);

  // I2C configurtion
  I2C_InitStruct.broadack = I2C_BROAD_ACK_DISABLE;
  I2C_InitStruct.master = I2C_MASTER_MODE_ENABLE;
  I2C_InitStruct.slave = I2C_SLAVE_MODE_DISABLE;
  I2C_InitStruct.speedclock = I2C_SPEED_RATE;
  I2C_Init(&I2C_InitStruct);

  NVIC_SetPriority(I2C_IRQn, 2);
  NVIC_EnableIRQ(I2C_IRQn);

  I2C_Cmd(ENABLE);
}

uint32_t eeprom_24c02_random_read(uint8_t addr, uint8_t* pdata)
{
  devAddress = I2C_DEV_ADDR & (~0x01);
  regAddress = addr;
  dataDirection = I2C_DATA_Read;
  pTxBuf=0;  txSize=0;
  pRxBuf=pdata;     rxSize=1;

  /* Reset STA, STO, SI, AA */
  I2C_CR_CLR(I2C_CR_FLAG);

  /* Set Start Signal */
  I2C_CR_SET(I2C_CR_STA);

  masterStatus = I2C_MASTATUS_IDLE;
  while(masterStatus == I2C_MASTATUS_IDLE);

  pTxBuf=0;   txSize=0;
  pRxBuf=0;   rxSize=0;

  if (masterStatus == I2C_MASTATUS_OK)
  {
    return 0;
  }
  return 1;
}

uint32_t eeprom_24c02_sequential_read(uint8_t addr, uint8_t* pbuf, uint32_t len)
{
  devAddress = I2C_DEV_ADDR & (~0x01);
  regAddress = addr;
  dataDirection = I2C_DATA_Read;
  pTxBuf=0;  txSize=0;
  pRxBuf=pbuf;     rxSize=len;

  /* Reset STA, STO, SI, AA */
  I2C_CR_CLR(I2C_CR_FLAG);

  /* Set Start Signal */
  I2C_CR_SET(I2C_CR_STA);

  masterStatus = I2C_MASTATUS_IDLE;
  while(masterStatus == I2C_MASTATUS_IDLE);

  pTxBuf=0;   txSize=0;
  pRxBuf=0;   rxSize=0;

  if (masterStatus == I2C_MASTATUS_OK)
  {
    return 0;
  }
  return 1;
}

uint32_t eeprom_24c02_byte_write(uint8_t addr, uint8_t data)
{
  devAddress = I2C_DEV_ADDR & (~0x01);
  regAddress = addr;
  dataDirection = I2C_DATA_Write;
  pTxBuf=&data;  txSize=1;
  pRxBuf=0;     rxSize=0;

  /* Reset STA, STO, SI, AA */
  I2C_CR_CLR(I2C_CR_FLAG);

  /* Set Start Signal */
  I2C_CR_SET(I2C_CR_STA);

  masterStatus = I2C_MASTATUS_IDLE;
  while(masterStatus == I2C_MASTATUS_IDLE);

  pTxBuf=0;   txSize=0;
  pRxBuf=0;   rxSize=0;

  if (masterStatus == I2C_MASTATUS_OK)
  {
    return 0;
  }
  return 1;
}

uint32_t eeprom_24c02_page_write(uint8_t addr, uint8_t* pdata, uint8_t len)
{
  devAddress = I2C_DEV_ADDR & (~0x01);
  regAddress = addr;
  dataDirection = I2C_DATA_Write;
  pTxBuf=pdata;  txSize=len;
  pRxBuf=0;     rxSize=0;

  /* Reset STA, STO, SI, AA */
  I2C_CR_CLR(I2C_CR_FLAG);

  /* Set Start Signal */
  I2C_CR_SET(I2C_CR_STA);

  masterStatus = I2C_MASTATUS_IDLE;
  while(masterStatus == I2C_MASTATUS_IDLE);

  pTxBuf=0;   txSize=0;
  pRxBuf=0;   rxSize=0;

  if (masterStatus == I2C_MASTATUS_OK)
  {
    return 0;
  }
  return 1;
}

uint32_t eeprom_24c02_probe(void)
{
  devAddress = I2C_DEV_ADDR & (~0x01);
  dataDirection = I2C_DATA_Probe;
  pTxBuf=0;   txSize=0;
  pRxBuf=0;   rxSize=0;

  /* Reset STA, STO, SI, AA */
  I2C_CR_CLR(I2C_CR_FLAG);

  /* Set Start Signal */
  I2C_CR_SET(I2C_CR_STA);

  masterStatus = I2C_MASTATUS_IDLE;
  while(masterStatus == I2C_MASTATUS_IDLE);   //Wait for finished

  I2C_CR_SET(I2C_CR_STO);

  if (masterStatus == I2C_MASTATUS_OK)
  {
    return 0;
  }
  return 1;
}

void I2C_IRQHandler(void)
{
  uint32_t stat;
  stat = (I2C->SR & 0xFF);
  
  switch(stat)
  {
  case I2C_STAT_MASTER_TX_START:
    I2C->DATA = devAddress;
    I2C_CR_CLR(I2C_CR_SI | I2C_CR_STA);
    break;

  case I2C_STAT_MASTER_TX_RESTART:
    I2C->DATA = (devAddress | 0x01);
    I2C_CR_CLR(I2C_CR_SI | I2C_CR_STA);
    break;

  case I2C_STAT_MASTER_TX_SLAW_ACK:
    if (dataDirection == I2C_DATA_Probe)
    {
      masterStatus = I2C_MASTATUS_OK;
      I2C_CR_SET(I2C_CR_STO);
    }
    else
    {
      I2C->DATA = regAddress;
    }
    I2C_CR_CLR(I2C_CR_SI);
    break;

  case I2C_STAT_MASTER_TX_SLAW_NOACK:
    masterStatus = I2C_MASTATUS_NOACK;
    I2C_CR_SET(I2C_CR_STO);
    I2C_CR_CLR(I2C_CR_SI);
    break;
  
  case I2C_STAT_MASTER_TX_DATA_ACK:
    if (dataDirection == I2C_DATA_Write)
    {
      if(txSize > 0)
      {
        I2C->DATA = *pTxBuf++;
        txSize--;
      }
      else
      {
        masterStatus = I2C_MASTATUS_OK;
        I2C_CR_SET(I2C_CR_STO);
      }
    }
    else if (dataDirection == I2C_DATA_Read)
    {
      I2C_CR_SET(I2C_CR_STA);
    }
    I2C_CR_CLR(I2C_CR_SI);
    break;

  case I2C_STAT_MASTER_TX_DATA_NOACK:
    masterStatus = I2C_MASTATUS_NOACK;
    I2C_CR_SET(I2C_CR_STO);
    I2C_CR_CLR(I2C_CR_SI);
    break;

  case I2C_STAT_MASTER_RX_SLAR_ACK:
    if (dataDirection == I2C_DATA_Read)
    {
      if(--rxSize > 0)
      {
        I2C_CR_SET(I2C_CR_AA);
      }
      else
      {
        I2C_CR_CLR(I2C_CR_AA);
      }
    }
    I2C_CR_CLR(I2C_CR_SI);
    break;
  
  case I2C_STAT_MASTER_RX_SLAW_NOACK:
    masterStatus = I2C_MASTATUS_NOACK;
    I2C_CR_SET(I2C_CR_STO);
    I2C_CR_CLR(I2C_CR_SI);
    break;

  case I2C_STAT_MASTER_RX_DATA_ACK:
    /* temp = I2C->DATA; */
    *pRxBuf++ = I2C->DATA;
    if(rxSize-- > 0)
    {
      I2C_CR_SET(I2C_CR_AA);
    }
    else
    {
      I2C_CR_CLR(I2C_CR_AA);
    }
    I2C_CR_CLR(I2C_CR_SI);
    break;

  case I2C_STAT_MASTER_RX_DATA_NOACK:
    *pRxBuf++ = I2C->DATA;
    masterStatus = I2C_MASTATUS_OK;
    I2C_CR_SET(I2C_CR_STO);
    I2C_CR_CLR(I2C_CR_SI);
    break;

  default:
    masterStatus = I2C_MASTATUS_ERROR;
    I2C_CR_SET(I2C_CR_STO);
    I2C_CR_CLR(I2C_CR_SI);
    break;
  }
}
