Project

General

Profile

RE: I2C issues on SLX45 ยป fpga_i2c.c

Conor O, 02/27/2013 08:01 AM

 
/*
* 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);


    (1-1/1)