/******************************************************************************
*     $Date: 2004/11/19 20:16:25 $
*     $RCSfile: iic_eeprom.c,v $
*******************************************************************************

*******************************************************************************
*
*     XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS"
*     SOLELY FOR USE IN DEVELOPING PROGRAMS AND SOLUTIONS FOR
*     XILINX DEVICES.  BY PROVIDING THIS DESIGN, CODE, OR INFORMATION
*     AS ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION
*     OR STANDARD, XILINX IS MAKING NO REPRESENTATION THAT THIS
*     IMPLEMENTATION IS FREE FROM ANY CLAIMS OF INFRINGEMENT,
*     AND YOU ARE RESPONSIBLE FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE
*     FOR YOUR IMPLEMENTATION.  XILINX EXPRESSLY DISCLAIMS ANY
*     WARRANTY WHATSOEVER WITH RESPECT TO THE ADEQUACY OF THE
*     IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OR
*     REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM CLAIMS OF
*     INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
*     FOR A PARTICULAR PURPOSE.
*     
*     (c) Copyright 2004 Xilinx, Inc.
*     All rights reserved.
*
******************************************************************************/
#include "xbasic_types.h"
#include "xparameters.h"
#include "xuartns550_l.h"
#include "sleep.h"
#include "stdio.h"

#define UART_BASEADDR  XPAR_OPB_UART16550_0_BASEADDR
#define UART_CLOCK     XPAR_XUARTNS550_CLOCK_HZ
#if !SIM
#define UART_BAUDRATE 9600                      /* real hardware */
#else
#define UART_BAUDRATE (UART_CLOCK / 16 / 3)     /* simulation */
#endif

// Macros to send data to IIC via bit bang
#define SEND0 iic_24lc04_lowest(0,0,0,1)
#define SEND1 iic_24lc04_lowest(1,1,0,1)
#define START iic_24lc04_lowest(1,0,1,1)
#define STOP iic_24lc04_lowest(1,0,0,0);iic_24lc04_lowest(0,1,1,1);iic_24lc04_lowest(1,1,0,1);iic_24lc04_lowest(1,1,0,1)
#define ACK iic_24lc04_lowest(1,1,0,1)
#define IDLE iic_24lc04_lowest(1,1,1,1)
#define NOACK ACK

// Warning - Allowing Writes Will Erase The Contents of IIC!!!
//#define WRITE_OK

// Low Level bit bang routine for SDA/SCL
Xuint32 iic_24lc04_lowest (int sda0, int sda1, int scl0, int scl1)
{
	Xuint32 adr = 0x90000008;
	Xuint32 data;
	Xuint32 old_reg;

    old_reg = XIo_In32(adr);
	XIo_Out32(adr, old_reg & 0xFFFFFFFC);

    old_reg = XIo_In32(adr+4);
	XIo_Out32(adr+4, (old_reg & 0xFFFFFFFC) | 0x00000040 | ((sda0)<<1) | scl0);
	usleep(10);
	XIo_Out32(adr+4, (old_reg & 0xFFFFFFFC) | 0x00000040 | ((sda1)<<1) | scl1);
	usleep(10);

	data = (XIo_In32(adr) >> 1);
	return data;
}

void iic_24lc04_send_data (Xuint32 data)
{
  if (data)
    SEND1;
  else
    SEND0;
}


// Write to IIC EEPROM, lower 9 bits of address and lower 8 bits of data are used
void iic_24lc04_write (Xuint32 addr, Xuint32 data)
{
    int i;

	IDLE;

	// Write
	START;
	// 0xAC if (addr[8] == 0) else 0xAE
	SEND1;SEND0;SEND1;SEND0;SEND1;SEND1;
    iic_24lc04_send_data (addr & 0x100);
	SEND0;
	ACK;

	// Address B5
    for (i=0; i<8; i++)
	  {
        iic_24lc04_send_data ((addr << i) & 0x80);
	  }
//	SEND1;SEND0;SEND1;SEND1;SEND0;SEND1;SEND0;SEND1;
	ACK;

	// Data 0x96
    for (i=0; i<8; i++)
	  {
        iic_24lc04_send_data ((data << i) & 0x80);
	  }
//	SEND1;SEND0;SEND0;SEND1;SEND0;SEND1;SEND1;SEND0;
	ACK;
	STOP;

	// Wait for write to complete
	IDLE;
	usleep(20000);

}


// Read from IIC EEPROM, lower 9 bits of address are used, 8 bits of data returned
Xuint32 iic_24lc04_read (Xuint32 addr)
{
    Xuint32 read_data;
    int i;

	IDLE;
	// Read
	START;
	// 0xAC if (addr[8] == 0) else 0xAE
	SEND1;SEND0;SEND1;SEND0;SEND1;SEND1;
    iic_24lc04_send_data (addr & 0x100);
	SEND0;

	ACK;

    for (i=0; i<8; i++)
	  {
        iic_24lc04_send_data ((addr << i) & 0x80);
	  }
	ACK;

    SEND1; // to resync on start bit
	START;

	// 0xAD if (addr[8] == 0) else 0xAF
	SEND1;SEND0;SEND1;SEND0;SEND1;SEND1;
    iic_24lc04_send_data (addr & 0x100);
	SEND1;

	// Data 0xFF
	ACK;
    read_data = 0;
	for (i=8; i>0; i--) {
		read_data = (read_data << 1) | (SEND1 & 0x1);
	}
	// No Ack
	NOACK;
	STOP;
    return (read_data);
}

int main(void)
{
    Xuint32 read_data;
	int i;
    int test_error = 0; // 0 = OK, 1 = Error

    XUartNs550_SetBaud(UART_BASEADDR, UART_CLOCK, UART_BAUDRATE);
    XUartNs550_mSetLineControlReg(UART_BASEADDR, XUN_LCR_8_DATA_BITS);

	xil_printf("\n\r\n\rBitbang IIC EEPROM test\n\r");
#ifdef WRITE_OK
	xil_printf("Writing all Zeros to IIC EEPROM\n\r");
    for (i = 0; i < 0x200; i++)
      iic_24lc04_write (i, 0);

	xil_printf("Reading back from IIC EEPROM\n\r");
    for (i = 0; i < 0x200; i++)
	  {
      read_data = iic_24lc04_read (i);
      xil_printf ("%2x ", read_data);
      if (read_data != 0x00) test_error = 1;
      }

	xil_printf("\n\rWriting 0x00->0xFF then 0xFF->0x00 to IIC EEPROM\n\r");
    for (i = 0; i < 0x100; i++)
      iic_24lc04_write (i, i);
    for (i = 0x100; i < 0x200; i++)
      iic_24lc04_write (i, 0x1FF - i);
#endif
	xil_printf("Reading back from IIC EEPROM\n\r");
    for (i = 0; i < 0x200; i++)
	  {
      read_data = iic_24lc04_read (i);
      xil_printf ("%2x ", read_data);
      if (i < 0x100)
	    {
        if (read_data != i) test_error = 1;
		}
      else
	    {
        if (read_data != (0x1FF-i)) test_error = 1;
		}
      }
#ifdef WRITE_OK
    if (test_error)
      xil_printf ("\r\nERROR: IIC TEST Unsuccessful.\r\n");
	else
	  xil_printf ("\r\nIIC TEST Passes - OK.\r\n");
#endif
	while (1) {
		IDLE;
		}

    return 0;
}


