/*
 * i2c.c
 *
 *  Created on: Apr 20, 2010
 *      Author: Mike Williamson
 */

#include "common/fpga/fpga_i2c.h"
#include "common/fpga/core_ids.h"
#include "fpga.h"

#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/i2c.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Mike Williamson <michael.williamson@criticallink.com");
MODULE_DESCRIPTION("Driver for MityDSP-L138 FPGA Based I2C Interface");

/**
 *  this is the device driver specific parameters tied to each i2c device in the system
 *  (the object data)
 */
struct fpga_i2c {
	struct i2c_adapter adapter;
};

/*
 * Low level master read/write transaction. This function is called
 * from i2c_xfer.
 *
 * \param[in] adap pointer to our adaptor device
 * \param[in] msg message to send
 * \param[in] stop true if this is the last message in a set
 *
 * \return number of bytes transmitted, or < 0 error condition
 */
static int
fpga_i2c_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, int stop)
{
	int i, slp_cnt;
	tuI2cCSR CSR;
	tuI2cSAR SAR;
	unsigned short  *lpBaseReg;
	struct device *dev;
	struct fpga_i2c *driver_data;
	struct fpga_device* fpgadev;

	dev = i2c_get_adapdata(adap);
	fpgadev = to_fpga_device(dev);
	driver_data = dev_get_drvdata(dev);
	lpBaseReg = (unsigned short*)fpgadev->baseaddr;

	dev_dbg(dev, "Msg Transfer - R/W = %d, len = %d\n", msg->flags & I2C_M_RD ? 1 : 0, msg->len);

	/* Can't handle 10 bit addresses yet */
	if (msg->flags & I2C_M_TEN)
	{
		dev_err(dev, "Msg Transfer 10 Bit Address Requested\n");
		return -EIO;
	}

	if (msg->len > FPGA_I2C_FIFO_DEPTH)
	{
		dev_err(dev, "Msg Transfer Message Length too big, %d\n", msg->len);
		return -EIO;
	}

	SAR.mnWord = 0;
	SAR.msBits.mnSlaveAddr = msg->addr;

	lpBaseReg[gnSAR_OFFSET] = SAR.mnWord;

	lpBaseReg[gnCNT_OFFSET] = msg->len;

	slp_cnt = 0;

	/** TODO - if transfer length is > 32 we need to
	 *  loop in multiples of 32 (without setting stop)
	 *  until we're done...
	 */
	if (msg->flags & I2C_M_RD)
	{
		/* Handle Bus Read */
		CSR.mnWord = 0;
		CSR.msBits.mbFifoReset = 1;
		lpBaseReg[gnCSR_OFFSET] = CSR.mnWord;
		wmb();

		CSR.mnWord = 0;
		CSR.msBits.mb10Bit = 0;
		CSR.msBits.mbRnW = 1;
		CSR.msBits.mbStop = stop;
		CSR.msBits.mbDone = 1;
		CSR.msBits.mbGo = 1;
		lpBaseReg[gnCSR_OFFSET] = CSR.mnWord;
		wmb();

		/* wait until done */
		CSR.mnWord = lpBaseReg[gnCSR_OFFSET];

		while (!CSR.msBits.mbDone) {
			msleep(1);
			rmb();
			CSR.mnWord = lpBaseReg[gnCSR_OFFSET];
			if (++slp_cnt > 250)
				break;
		};

		if (slp_cnt > 250) {
			dev_dbg(dev, "Timeout on write request\n");
			return -EIO;
		}

		/* go fetch the data */
		for (i = 0; i < msg->len; i++)
		{
			tuI2cFDR DataReg;
			DataReg.mnWord = lpBaseReg[gnFDR_OFFSET];
			msg->buf[i] = DataReg.msBits.mnData;
			rmb();
		}

	}
	else
	{
		/* clear the buffers */
		dev_dbg(dev, "Loading buffer with %d bytes\n", msg->len);

		CSR.mnWord = 0;
		CSR.msBits.mbFifoReset = 1;
		lpBaseReg[gnCSR_OFFSET] = CSR.mnWord;
		wmb();

		CSR.msBits.mbFifoReset = 0;
		lpBaseReg[gnCSR_OFFSET] = CSR.mnWord;
		wmb();

		/* load the buffer */
		for (i = 0; i < msg->len; i++)
		{
			tuI2cFDR DataReg;
			DataReg.mnWord = 0;
			DataReg.msBits.mnData = msg->buf[i];
			lpBaseReg[gnFDR_OFFSET] = DataReg.mnWord;
			wmb();
		}

		/* Handle Bus Write */
		CSR.mnWord = 0;
		CSR.msBits.mb10Bit = 0;
		CSR.msBits.mbRnW = 0;
		CSR.msBits.mbStop = stop;
		CSR.msBits.mbDone = 1;
		CSR.msBits.mbGo = 1;
		lpBaseReg[gnCSR_OFFSET] = CSR.mnWord;
		wmb();

		/* wait until done */
		CSR.mnWord = lpBaseReg[gnCSR_OFFSET];

		while (!CSR.msBits.mbDone) {
			msleep(1);
			rmb();
			CSR.mnWord = lpBaseReg[gnCSR_OFFSET];
			if (++slp_cnt > 250)
				break;
		};

		if (slp_cnt > 250) {
			dev_dbg(dev, "Timeout on write request\n");
			return -EIO;
		}
	}

	/** check status for ack errors, etc. */
	CSR.mnWord = lpBaseReg[gnCSR_OFFSET];
	if (CSR.msBits.mbAckErr) {
		dev_dbg(dev, "Ack Error\n");
		return -EIO;
	}

	dev_dbg(dev, "transfer returning %d\n", msg->len);
	return msg->len;
}

/*
 * Prepare controller for a transaction and call i2c_davinci_xfer_msg
 *
 * \param[in] adap pointer to our adaptor device
 * \param[in] msgs pointer to a list of messages to send
 * \param[in] num number of messages to transfer
 */
static int
fpga_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
{
	struct device *dev = i2c_get_adapdata(adap);
	int i;
	int ret;

	dev_dbg(dev, "%s: msgs: %d\n", __func__, num);

/*
	ret = i2c_wait_bus_not_busy(dev, 1);
	if (ret < 0) {
		dev_warn(dev->dev, "timeout waiting for bus ready\n");
		return ret;
	}
*/

	for (i = 0; i < num; i++) {
		ret = fpga_i2c_xfer_msg(adap, &msgs[i], (i == (num - 1)));
		dev_dbg(dev, "%s [%d/%d] ret: %d\n", __func__, i + 1, num, ret);
		if (ret < 0)
		{
			dev_dbg(dev, "Failed to transfer message : %d\n", ret);
			return ret;
		}
	}

	return num;
}

static u32 fpga_i2c_func(struct i2c_adapter *adap)
{
	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
}

static struct i2c_algorithm i2c_algo = {
	.master_xfer	= fpga_i2c_xfer,
	.functionality	= fpga_i2c_func,
};

static struct attribute *fpga_i2c_attributes[] = {
		/* &dev_attr_pen_down.attr, */
		NULL,
};

static struct attribute_group fpga_i2c_attr_group = {
		.attrs = fpga_i2c_attributes,
};

/**
 * IRQ handler called when an ADS7843 core is asserting an interrupt
 * condition.  This method is called within the context of an ISR, and
 * must be atomic.
 *
 * \param[in] dev the device issuing the interrupt.
 * \return 0
 */
static int fpga_i2c_IrqHandler(struct fpga_device* dev)
{
	struct i2c     *driver_data;
	unsigned short  *lpBaseReg;

	driver_data = dev_get_drvdata(&dev->dev);
	lpBaseReg = (unsigned short*)dev->baseaddr;

	/* TODO - interrupts not yet designed into core */

	return 0;
}

/**
 * This routine is called when a device is removed from the FPGA bus.
 *
 * \param[in] dev pointer to the device being removed.
 */
static int fpga_i2c_remove(struct device *dev)
{
	int rv = 0;
	struct fpga_i2c *driver_data;
	struct fpga_device* fpgadev = to_fpga_device(dev);
	driver_data = dev_get_drvdata(dev);

	dev_dbg(dev, "i2c_remove entered\n");

	i2c_del_adapter(&driver_data->adapter);

	sysfs_remove_group(&dev->kobj, &fpga_i2c_attr_group);

	/* make sure IRQ is disabled */
	enable_irq_vec(fpgadev->coreversion.ver0.bits.level,
				   fpgadev->coreversion.ver0.bits.vector,
				   0);

	kfree(driver_data);

	dev_dbg(dev, "i2c_remove completed\n");
	return rv;
}

static int fpga_i2c_probe(struct device *dev);

/**
 * The driver object.  The information here must be common for
 * all of the potential drivers in the system.
 */
static struct fpga_driver fpga_i2c_driver = {
	.id 		= CORE_ID_I2C, /** must match value in core_ids.h */
	.version 	= "I2C Driver 1.0",
	.IrqHandler 	= fpga_i2c_IrqHandler,
	.probe 		= fpga_i2c_probe,
	.remove 	= fpga_i2c_remove,
	.driver 	= {
		.name 	= "fpga_i2c",
		.owner 	= THIS_MODULE,
	},
};

int adapter_number = 2;
module_param (adapter_number, int, S_IRUSR | S_IRGRP | S_IROTH);
MODULE_PARM_DESC (adapter_number, "The base adaptor number for "
		                          "new I2C busses (default 2)");

/**
 * The i2c_probe routine is called after the i2c driver is successfully
 * matched to an FPGA core with the same core ID.
 *
 * \param[in] dev device within an fpga_device structure.
 * return 0 on successful probe / initialization.
 */
static int fpga_i2c_probe(struct device *dev)
{
	int rv = 0;
	struct i2c_adapter* adap;
	struct fpga_i2c  *driver_data = NULL;
	unsigned short     *lpBaseReg = NULL;

	struct fpga_device* fpgadev = to_fpga_device(dev);

	lpBaseReg = (unsigned short*)fpgadev->baseaddr;

	dev_dbg(dev, "i2c_probe() entered\n");

	driver_data = kzalloc(sizeof(struct fpga_i2c), GFP_KERNEL);
	if (!driver_data)
	{
		rv = -ENOMEM;
		goto probe_bail;
	}

	rv = sysfs_create_group(&dev->kobj, &fpga_i2c_attr_group);
	if (rv)
	{
		dev_err(dev, "i2c_probe() failed to add attributes group - %d\n", rv);
		goto probe_bail_free_driver;
	}

	dev_set_drvdata(dev, driver_data);

	adap = &driver_data->adapter;
	i2c_set_adapdata(adap, dev);
	adap->owner = THIS_MODULE;
	adap->class = I2C_CLASS_HWMON;
	strlcpy(adap->name, "MityDSP FPGA I2C adapter", sizeof(adap->name));
	adap->algo = &i2c_algo;
	adap->dev.parent = dev;
	adap->timeout = FPGA_I2C_TIMEOUT;

	adap->nr = adapter_number++;
	rv = i2c_add_numbered_adapter(adap);
	if (rv) {
		dev_err(dev, "failure adding adapter\n");
		goto probe_bail_free_adapt;
	}

	/* Set the frequency */
	lpBaseReg[gnDIV_OFFSET] = 0xFF; /* TODO! */
	wmb();

	enable_irq_vec(fpgadev->coreversion.ver0.bits.level,
			       fpgadev->coreversion.ver0.bits.vector,
			       1);

	return rv;

probe_bail_free_adapt:


probe_bail_free_driver:
	kfree(driver_data);

probe_bail:
	return rv;
}

/**
 * Called when the module containing this driver is loaded.
 */
static int __init fpga_i2c_init(void)
{
	int ret;

	ret = register_fpga_driver(&fpga_i2c_driver);
	if (ret)
	{
		printk(KERN_ALERT "Unable to register i2c_driver\n");
		goto exit_bail;
	}

	return 0;

exit_bail: /* Uh-Oh */
	return -1;
}

/**
 * Called when the module containing this driver is unloaded from kernel.
 */
static void fpga_i2c_exit(void)
{
	driver_unregister(&fpga_i2c_driver.driver);
}

module_init(fpga_i2c_init);
module_exit(fpga_i2c_exit);


