/*
 I2C read/write functions
 
 Copyright (c) 2008 Dimtel, Inc., All Rights Reserved

 $Id: i2c.c,v 1.1 2012/11/11 01:24:32 dim Exp $
 $Date: 2012/11/11 01:24:32 $
 $Author: dim $
 $Revision: 1.1 $
 $Log: i2c.c,v $
 Revision 1.1  2012/11/11 01:24:32  dim
 Moved from BxB, adapted to compile for both LLRF4 and IGP12

 Revision 1.1  2011/12/27 19:31:46  dim
 Updated to remove deprecated constructs (at, sfr, xdata, sbit)


*/

#include "fx2regs.h"
#include "common.h"
#include "i2c.h"

uchar i2c_read(uchar addr, __xdata uchar *buf, uchar len)
{
  __bit retry = 0;
  volatile uchar foo;
  uchar cnt;
  __xdata uchar *buf1;

  if (len == 0)
    return 1; /* Do nothing */

  /* TRM: 13.4.4, 13-16 */
  /* Here we wait for the STOP = 0 (step 15) at the beginning. This
     way the transfer can complete without wasting CPU cycles */
  while (I2CS & bmI2C_STOP);
  
  do
  {
    /* Set up pointer and counter */
    buf1 = buf; cnt = len;
    
    /* Step 1: START = 1, contention not expected */
    I2CS = bmI2C_START;
  
    /* Step 2: 7-bit address and 1 for reading */
    I2DAT = (addr << 1) | 1;
  
    /* STEP 3: Wait for DONE */
    while ((I2CS & bmI2C_DONE) == 0);
  
    /* STEP 4: check ACK */
    if ((I2CS & bmI2C_ACK) == 0)
    {
      /* No answer received, fail */
      I2CS |= bmI2C_STOP;
      return 0;
    }
    
    if (I2CS & bmI2C_BERR)
      retry = 1;
    else
    {
      /* STEP 10: before reading second-to-last byte set LASTRD */
      if (cnt == 1)
        I2CS |= bmI2C_LASTRD;

      /* STEP 5: read I2DAT */
      foo = I2DAT;
  
      while (--cnt)
      {
        /* STEP 6: wait for DONE */
        while ((I2CS & bmI2C_DONE) == 0);
  
        if (I2CS & bmI2C_BERR)
        {
          retry = 1;
          break;
        }
        else
        {
          /* STEP 10: before reading second-to-last byte set LASTRD */
          if (cnt == 1)
            I2CS |= bmI2C_LASTRD;
          
          /* STEP 7/11: read the data */
          *buf1++ = I2DAT;
        }
      }
      
      /* Make sure we came out OK from the cycle (no BERR) */
      if (retry == 0)
      {
        /* STEP 12: wait for DONE */
        while ((I2CS & bmI2C_DONE) == 0);
                        
        if (I2CS & bmI2C_BERR)
          retry = 1;
        else
        {
          I2CS |= bmI2C_STOP;
          *buf1 = I2DAT;
        }
      }
    }
  } while (retry);
  
  return 1;
}

uchar i2c_write(uchar addr, __xdata uchar *buf, uchar len)
{
  __bit retry = 0;
  uchar cnt;
  __xdata uchar *buf1;

  /* TRM: 13.4.3, 13-16 */
  while (I2CS & bmI2C_STOP);
  
  do
  {
    /* Set up counters, etc */
    cnt = len; buf1 = buf;
    
    /* STEP 1: set START = 1 */
    I2CS = bmI2C_START;
  
    /* STEP 2: 7-bit address, LSB 0 for writing */
    I2DAT = addr << 1;
  
    /* STEP 3: wait for DONE */
    while ((I2CS & bmI2C_DONE) == 0);
    
    if (I2CS & bmI2C_BERR)
      retry = 1;
    else
    {
      /* STEP 4: check ACK */ 
      if ((I2CS & bmI2C_ACK) == 0)
      {
        /* No answer received, fail */
        I2CS |= bmI2C_STOP;
        return 0;
      }
      
      while (cnt--)
      {
        /* STEP 5: write data */
        I2DAT = *buf1++;
        
        /* STEP 6: wait for DONE */
        while ((I2CS & bmI2C_DONE) == 0);
        
        if (I2CS & bmI2C_BERR)
        {
          retry = 1;
          break;
        }
        
        if ((I2CS & bmI2C_ACK) == 0)
        {
          I2CS |= bmI2C_STOP;
          return 0;
        }
      }
    }
  } while (retry);
  
  I2CS |= bmI2C_STOP;
      
  return 1;
}
