#include <stdio.h>

#include "regs/dds_regs.h"
#include "speclib.h"

#define BASE_ADDR 0x80000
#define EEPROM_ADDR 0x50

#define M_SDA_OUT(x) 														\
{																			\
	if(x)																	\
		spec_writel(dev, spec_readl(dev, BASE_ADDR + DDS_REG_I2CR) | DDS_I2CR_SDA_OUT, BASE_ADDR + DDS_REG_I2CR); 	\
    else																	\
		spec_writel(dev, spec_readl(dev, BASE_ADDR + DDS_REG_I2CR) & (~DDS_I2CR_SDA_OUT), BASE_ADDR + DDS_REG_I2CR); \
		udelay(10);															\
}

#define M_SCL_OUT(x) 														\
{																			\
    if(x)																	\
		spec_writel(dev, spec_readl(dev, BASE_ADDR + DDS_REG_I2CR) | DDS_I2CR_SCL_OUT, BASE_ADDR + DDS_REG_I2CR); 	\
    else																	\
		spec_writel(dev, spec_readl(dev, BASE_ADDR + DDS_REG_I2CR) & (~DDS_I2CR_SCL_OUT), BASE_ADDR + DDS_REG_I2CR); \
		udelay(10); 														\
}

#define M_SDA_IN ((spec_readl(dev, BASE_ADDR + DDS_REG_I2CR) & DDS_I2CR_SDA_IN) ? 1 : 0)

static void mi2c_start(void *dev)
{
	M_SDA_OUT(0);
	M_SCL_OUT(0);
}

static void mi2c_repeat_start(void *dev)
{
	M_SDA_OUT(1);
	M_SCL_OUT(1);
	M_SDA_OUT(0);
	M_SCL_OUT(0);
}

static void mi2c_stop(void *dev)
{
	M_SDA_OUT(0);
	M_SCL_OUT(1);
	M_SDA_OUT(1);
}

 int mi2c_put_byte(void *dev, unsigned char data)
{
	char i;
	unsigned char ack;

	for (i=0;i<8;i++, data<<=1)
    {
		M_SDA_OUT(data&0x80);
		M_SCL_OUT(1);
		M_SCL_OUT(0);
    }

	M_SDA_OUT(1);
	M_SCL_OUT(1);

	ack = M_SDA_IN;	/* ack: sda is pulled low ->success.	 */

	M_SCL_OUT(0);
	M_SDA_OUT(0);

	return ack!=0 ? -1 : 0;
}

void mi2c_get_byte(void *dev, unsigned char *data, int ack)
{

	int i;
	unsigned char indata = 0;

	/* assert: scl is low */
	M_SCL_OUT(0);
	M_SDA_OUT(1);
	for (i=0;i<8;i++)
    {
		M_SCL_OUT(1);
		indata <<= 1;
		if ( M_SDA_IN ) indata |= 0x01;
		M_SCL_OUT(0);
    }

	M_SDA_OUT((ack ? 0 : 1));
	M_SCL_OUT(1);
	M_SCL_OUT(0);
	M_SDA_OUT(0);

	*data= indata;
}

void mi2c_init(void *dev)
{

	M_SCL_OUT(1);
	M_SDA_OUT(1);
}

void mi2c_scan(void *dev)
{
 	int i;
 	for(i=0;i<256;i+=2)
 	{
 	 	mi2c_start(dev);
		if(!mi2c_put_byte(dev,i))
			dbg("Found device at 0x%x\n", i>>1);
		mi2c_stop(dev);
 	}
}

int eeprom_read(void *dev, uint8_t i2c_addr, uint32_t offset, uint8_t *buf, size_t size)
{
	int i;
	unsigned char c;
	for(i=0;i<size;i++)
	{
		mi2c_start(dev);
		if(mi2c_put_byte(dev, i2c_addr << 1) < 0)
		{
			mi2c_stop(dev);
			return -1;
		}

		mi2c_put_byte(dev, (offset >> 8) & 0xff);
		mi2c_put_byte(dev, offset & 0xff);
		offset++;
		mi2c_stop(dev);
		mi2c_start(dev);
		mi2c_put_byte(dev, (i2c_addr << 1) | 1);
		mi2c_get_byte(dev, &c, 0);
		//printf("readback: %x\n", c);
		*buf++ = c;
		mi2c_stop(dev);
 	}
 	return size;
}

int eeprom_write(void *dev, uint8_t i2c_addr, uint32_t offset, uint8_t *buf, size_t size)
{
	int i, busy;
	for(i=0;i<size;i++)
	{

	 	mi2c_start(dev);

	 	if(mi2c_put_byte(dev, i2c_addr << 1) < 0)
	 	{
		 	mi2c_stop(dev);
	 	 	return -1;
	  	}

		mi2c_put_byte(dev, (offset >> 8) & 0xff);
		mi2c_put_byte(dev, offset & 0xff);
		mi2c_put_byte(dev, *buf++);
		offset++;
		mi2c_stop(dev);

		int64_t ts = get_tics();
		do /* wait until the chip becomes ready */
		{
            mi2c_start(dev);
			busy = mi2c_put_byte(dev, i2c_addr << 1);
			mi2c_stop(dev);
		} while(busy && ((get_tics()-ts) < 1000000));
		if((get_tics()-ts) >= 1000000)
		{
			dbg("%s has timed out, the EEPROM doesn't become ready\n", __FUNCTION__);
			return -1;
		}
	}
 	return size;
}


int write_eeprom_from_file(void *dev, const char *filename, uint32_t offset)
{
    FILE *f;
    char *buf, *rbuf;
    int size;
    int rv=0;

    f = fopen(filename, "rb");
    if(!f)
	return -1;

    fseek(f, 0, SEEK_END);
    size = ftell(f);
    rewind(f);

    buf = malloc(size);
    rbuf = malloc(size);


    fread(buf, 1, size, f);

    fclose(f);


    eeprom_write(dev, EEPROM_ADDR, offset, buf, size);
    if(eeprom_read(dev, EEPROM_ADDR, offset, rbuf, size) != size)
	rv=-1;

    if(memcmp(buf, rbuf, size))
    {
//	dbg("%s: eeprom verification failed\n", __FUNCTION__);
	rv=-1;
    }

    free(buf);
    free(rbuf);

    return rv;
}

