#include "stdio.h"     
#include "unistd.h"    
#include "sys/ioctl.h"
#include "fcntl.h"     
#include "time.h"     
#include "asm/etraxgpio.h"

#define I2C_DATA_LINE			1<<6
#define I2C_CLOCK_LINE		1<<7


#ifndef IO_SETGET_INPUT
#define IO_SETGET_INPUT 	0x12
#endif

#ifndef IO_SETGET_OUTPUT
#define IO_SETGET_OUTPUT 	0x13
#endif


int i2c_fd;

// Software delay in us

void udelay(int us) {
  int a;
  int b;
  int delayvar=1111;

  for (b=0;b<33;b++) {
	  for (a=0;a<us;a++) {
	    delayvar*=3;
	    delayvar/=3;
	  }
	}  
}   

// Get the SDA line state

int i2c_getbit(void) {
	unsigned int value;
	value=ioctl(i2c_fd, _IO(ETRAXGPIO_IOCTYPE, IO_READBITS));
	if ((value&(I2C_DATA_LINE))==0) return 0;
	else return 1;
}	

// Set the SDA line state

void i2c_data(int state) {
	if (state==1) ioctl(i2c_fd, _IO(ETRAXGPIO_IOCTYPE, IO_SETBITS), I2C_DATA_LINE);
	else ioctl(i2c_fd, _IO(ETRAXGPIO_IOCTYPE, IO_CLRBITS), I2C_DATA_LINE);
}

// Set the SCL line state

void i2c_clk(int state) {
	if (state==1) ioctl(i2c_fd, _IO(ETRAXGPIO_IOCTYPE, IO_SETBITS), I2C_CLOCK_LINE);
	else ioctl(i2c_fd, _IO(ETRAXGPIO_IOCTYPE, IO_CLRBITS), I2C_CLOCK_LINE);
}

// Set the SDA line as output

void i2c_dir_out(void) {
 	int iomask;
	iomask = I2C_DATA_LINE;
 	ioctl(i2c_fd, _IO(ETRAXGPIO_IOCTYPE, IO_SETGET_OUTPUT), &iomask);
}

// Set the SDA line as input

void i2c_dir_in(void) {
 	int iomask;
	iomask = I2C_DATA_LINE;
 	ioctl(i2c_fd, _IO(ETRAXGPIO_IOCTYPE, IO_SETGET_INPUT), &iomask);
}


// Read a byte from I2C bus and send the ack sequence

unsigned char i2c_inbyte(void) {
	unsigned char value = 0;
	int bitvalue;
	int i;

	// Read data byte

	i2c_dir_in();

	for (i=0;i<8;i++) {
		i2c_clk(1);

		bitvalue = i2c_getbit();
		value |= bitvalue;
		if (i<7) value <<= 1;
		
		i2c_clk(0);
	}
	
	// Send Ack
	
	i2c_data(0);
	i2c_dir_out();
	i2c_data(0);

	i2c_clk(1);
	i2c_clk(0);

	udelay(100);	
	return value;
}


// Open the GPIOB dev 

int i2c_open(void) {
  i2c_fd = open("/dev/gpiob", O_RDWR);
	i2c_data(1);
 	i2c_dir_out();
	i2c_clk(1);
	i2c_data(1);
	udelay(100);
  return i2c_fd;
}

// Close the GPIOB dev 

void i2c_close(void) {
  close(i2c_fd);
}

// Send a start sequence to I2C bus

void i2c_start(void){
	i2c_data(0);
	i2c_clk(0);
}

// Send a stop sequence to I2C bus

void i2c_stop(void) {
	i2c_clk(1);
	i2c_data(1);
}

// Send a byte to the I2C bus and return the ack sequence from slave
// rtc
//	0 = Nack, 1=Ack

int i2c_outbyte(unsigned char x) {
	int i;
	int ack;

	i2c_clk(0);

	for (i=0;i<8;i++) {
		if (x & 0x80) i2c_data(1);
		else	i2c_data(0);
		i2c_clk(1);
		i2c_clk(0);
		x <<= 1;
	}
	
 	i2c_dir_in();
	i2c_clk(1);
	ack=i2c_getbit();
	i2c_clk(0);
	i2c_dir_out();
	
	if (ack==0) return 1;
	else return 0;
}

// Program example

int main(void) {
	int value;
	int ch;
	
	if (i2c_open()<0) {
    printf("i2c open error\n");
    return 1;
	}

	while (1) {
		for (ch=0;ch<4;ch++) {
			i2c_start();
			if (i2c_outbyte(0x92)==0) {
				printf("NACK received\n");
			}
			
			if (i2c_outbyte(ch)==0) {
				printf("NACK received\n");
			}
			i2c_stop();
					
			i2c_start();
			if (i2c_outbyte(0x93)==0) {
				printf("NACK received\n");
			}
	
			i2c_inbyte(); 					// Last conversion result
			value=i2c_inbyte(); // Current conversion result
			i2c_stop();
		
			printf("CH%d = %.2fv\n",ch,value*0.012941);
		}	
		printf("\n");
		sleep(1);
	}	
	
	i2c_close();
  return 0;
}	
