* Additional contributions by:
* Tony Lindgren <tony@atomide.com>
* Imre Deak <imre.deak@nokia.com>
- * Juha Yrjölä <juha.yrjola@nokia.com>
+ * Juha Yrjölä <juha.yrjola@solidboot.com>
* Syed Khasim <x0khasim@ti.com>
+ * Nishant Menon <nm@ti.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
#include <linux/completion.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
+#include <linux/io.h>
-#include <asm/io.h>
+/* I2C controller revisions */
+#define OMAP_I2C_REV_2 0x20
-/* Hack to enable zero length transfers and smbus quick until clean fix
- is available */
-#define OMAP_HACK
+/* I2C controller revisions present on specific hardware */
+#define OMAP_I2C_REV_ON_2430 0x36
+#define OMAP_I2C_REV_ON_3430 0x3C
/* timeout waiting for the controller to respond */
#define OMAP_I2C_TIMEOUT (msecs_to_jiffies(1000))
#define OMAP_I2C_BUFSTAT_REG 0x40
/* I2C Interrupt Enable Register (OMAP_I2C_IE): */
-#define OMAP_I2C_IE_XDR (1 << 14) /* TX Buffer draining int enable */
-#define OMAP_I2C_IE_RDR (1 << 13) /* RX Buffer draining int enable */
+#define OMAP_I2C_IE_XDR (1 << 14) /* TX Buffer drain int enable */
+#define OMAP_I2C_IE_RDR (1 << 13) /* RX Buffer drain int enable */
#define OMAP_I2C_IE_XRDY (1 << 4) /* TX data ready int enable */
#define OMAP_I2C_IE_RRDY (1 << 3) /* RX data ready int enable */
#define OMAP_I2C_IE_ARDY (1 << 2) /* Access ready int enable */
#define OMAP_I2C_SYSTEST_SDA_O (1 << 0) /* SDA line drive out */
#endif
-/* I2C System Status register (OMAP_I2C_SYSS): */
-#define OMAP_I2C_SYSS_RDONE (1 << 0) /* Reset Done */
+/* OCP_SYSSTATUS bit definitions */
+#define SYSS_RESETDONE_MASK (1 << 0)
+
+/* OCP_SYSCONFIG bit definitions */
+#define SYSC_CLOCKACTIVITY_MASK (0x3 << 8)
+#define SYSC_SIDLEMODE_MASK (0x3 << 3)
+#define SYSC_ENAWAKEUP_MASK (1 << 2)
+#define SYSC_SOFTRESET_MASK (1 << 1)
+#define SYSC_AUTOIDLE_MASK (1 << 0)
+
+#define SYSC_IDLEMODE_SMART 0x2
+#define SYSC_CLOCKACTIVITY_FCLK 0x2
-/* I2C System Configuration Register (OMAP_I2C_SYSC): */
-#define OMAP_I2C_SYSC_SRST (1 << 1) /* Soft Reset */
struct omap_i2c_dev {
struct device *dev;
* fifo_size==0 implies no fifo
* if set, should be trsh+1
*/
- unsigned rev1:1;
+ u8 rev;
unsigned b_hw:1; /* bad h/w fixes */
unsigned idle:1;
u16 iestate; /* Saved interrupt register */
return -ENODEV;
}
}
- /* For I2C operations on 2430 we need 96Mhz clock */
- if (cpu_is_omap2430()) {
- dev->fclk = clk_get(dev->dev, "i2chs_fck");
- if (IS_ERR(dev->fclk)) {
- if (dev->iclk != NULL) {
- clk_put(dev->iclk);
- dev->iclk = NULL;
- }
- dev->fclk = NULL;
- return -ENODEV;
- }
- } else {
- dev->fclk = clk_get(dev->dev, "i2c_fck");
- if (IS_ERR(dev->fclk)) {
- if (dev->iclk != NULL) {
- clk_put(dev->iclk);
- dev->iclk = NULL;
- }
- dev->fclk = NULL;
- return -ENODEV;
+
+ dev->fclk = clk_get(dev->dev, "i2c_fck");
+ if (IS_ERR(dev->fclk)) {
+ if (dev->iclk != NULL) {
+ clk_put(dev->iclk);
+ dev->iclk = NULL;
}
+ dev->fclk = NULL;
+ return -ENODEV;
}
+
return 0;
}
static void omap_i2c_unidle(struct omap_i2c_dev *dev)
{
+ WARN_ON(!dev->idle);
+
if (dev->iclk != NULL)
clk_enable(dev->iclk);
clk_enable(dev->fclk);
{
u16 iv;
+ WARN_ON(dev->idle);
+
dev->iestate = omap_i2c_read_reg(dev, OMAP_I2C_IE_REG);
omap_i2c_write_reg(dev, OMAP_I2C_IE_REG, 0);
- if (dev->rev1)
- iv = omap_i2c_read_reg(dev, OMAP_I2C_IV_REG);
- else
+ if (dev->rev < OMAP_I2C_REV_2) {
+ iv = omap_i2c_read_reg(dev, OMAP_I2C_IV_REG); /* Read clears */
+ } else {
omap_i2c_write_reg(dev, OMAP_I2C_STAT_REG, dev->iestate);
- /*
- * The wmb() is to ensure that the I2C interrupt mask write
- * reaches the I2C controller before the dev->idle store
- * occurs.
- */
- wmb();
+
+ /* Flush posted write before the dev->idle store occurs */
+ omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG);
+ }
dev->idle = 1;
clk_disable(dev->fclk);
if (dev->iclk != NULL)
unsigned long timeout;
unsigned long internal_clk = 0;
- if (!dev->rev1) {
- omap_i2c_write_reg(dev, OMAP_I2C_SYSC_REG, OMAP_I2C_SYSC_SRST);
+ if (dev->rev >= OMAP_I2C_REV_2) {
+ omap_i2c_write_reg(dev, OMAP_I2C_SYSC_REG, SYSC_SOFTRESET_MASK);
/* For some reason we need to set the EN bit before the
* reset done bit gets set. */
timeout = jiffies + OMAP_I2C_TIMEOUT;
omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, OMAP_I2C_CON_EN);
while (!(omap_i2c_read_reg(dev, OMAP_I2C_SYSS_REG) &
- OMAP_I2C_SYSS_RDONE)) {
+ SYSS_RESETDONE_MASK)) {
if (time_after(jiffies, timeout)) {
dev_warn(dev->dev, "timeout waiting "
"for controller reset\n");
}
msleep(1);
}
+
+ /* SYSC register is cleared by the reset; rewrite it */
+ if (dev->rev == OMAP_I2C_REV_ON_2430) {
+
+ omap_i2c_write_reg(dev, OMAP_I2C_SYSC_REG,
+ SYSC_AUTOIDLE_MASK);
+
+ } else if (dev->rev >= OMAP_I2C_REV_ON_3430) {
+ u32 v;
+
+ v = SYSC_AUTOIDLE_MASK;
+ v |= SYSC_ENAWAKEUP_MASK;
+ v |= (SYSC_IDLEMODE_SMART <<
+ __ffs(SYSC_SIDLEMODE_MASK));
+ v |= (SYSC_CLOCKACTIVITY_FCLK <<
+ __ffs(SYSC_CLOCKACTIVITY_MASK));
+
+ omap_i2c_write_reg(dev, OMAP_I2C_SYSC_REG, v);
+
+ }
}
omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, 0);
struct i2c_msg *msg, int stop)
{
struct omap_i2c_dev *dev = i2c_get_adapdata(adap);
-#ifdef OMAP_HACK
- u8 zero_byte = 0;
-#endif
int r;
u16 w;
dev_dbg(dev->dev, "addr: 0x%04x, len: %d, flags: 0x%x, stop: %d\n",
msg->addr, msg->len, msg->flags, stop);
-#ifndef OMAP_HACK
if (msg->len == 0)
return -EINVAL;
dev->buf = msg->buf;
dev->buf_len = msg->len;
-#else
-
- omap_i2c_write_reg(dev, OMAP_I2C_SA_REG, msg->addr);
- /* REVISIT: Remove this hack when we can get I2C chips from board-*.c
- * files
- * Sigh, seems we can't do zero length transactions. Thus, we
- * can't probe for devices w/o actually sending/receiving at least
- * a single byte. So we'll set count to 1 for the zero length
- * transaction case and hope we don't cause grief for some
- * arbitrary device due to random byte write/read during
- * probes.
- */
- if (msg->len == 0) {
- dev->buf = &zero_byte;
- dev->buf_len = 1;
- } else {
- dev->buf = msg->buf;
- dev->buf_len = msg->len;
- }
-#endif
-
omap_i2c_write_reg(dev, OMAP_I2C_CNT_REG, dev->buf_len);
/* Clear the FIFO Buffers */
omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, w);
+ /*
+ * Don't write stt and stp together on some hardware.
+ */
if (dev->b_hw && stop) {
- /* H/w behavior: dont write stt and stp together.. */
- while (omap_i2c_read_reg(dev, OMAP_I2C_CON_REG) & OMAP_I2C_CON_STT) {
- /* Dont do anything - this will come in a couple of loops at max*/
+ unsigned long delay = jiffies + OMAP_I2C_TIMEOUT;
+ u16 con = omap_i2c_read_reg(dev, OMAP_I2C_CON_REG);
+ while (con & OMAP_I2C_CON_STT) {
+ con = omap_i2c_read_reg(dev, OMAP_I2C_CON_REG);
+
+ /* Let the user know if i2c is in a bad state */
+ if (time_after(jiffies, delay)) {
+ dev_err(dev->dev, "controller timed out "
+ "waiting for start condition to finish\n");
+ return -ETIMEDOUT;
+ }
+ cpu_relax();
}
+
w |= OMAP_I2C_CON_STP;
w &= ~OMAP_I2C_CON_STT;
omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, w);
}
+
+ /*
+ * REVISIT: We should abort the transfer on signals, but the bus goes
+ * into arbitration and we're currently unable to recover from it.
+ */
r = wait_for_completion_timeout(&dev->cmd_complete,
OMAP_I2C_TIMEOUT);
dev->buf_len = 0;
omap_i2c_unidle(dev);
- if ((r = omap_i2c_wait_for_bb(dev)) < 0)
+ r = omap_i2c_wait_for_bb(dev);
+ if (r < 0)
goto out;
for (i = 0; i < num; i++) {
static u32
omap_i2c_func(struct i2c_adapter *adap)
{
-#ifndef OMAP_HACK
return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
-#else
- return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
-#endif
}
static inline void
return IRQ_HANDLED;
}
#else
-#define omap_i2c_rev1_isr 0
+#define omap_i2c_rev1_isr NULL
#endif
static irqreturn_t
if (stat & (OMAP_I2C_STAT_RRDY | OMAP_I2C_STAT_RDR)) {
u8 num_bytes = 1;
if (dev->fifo_size) {
- num_bytes = (stat & OMAP_I2C_STAT_RRDY) ? dev->fifo_size :
- omap_i2c_read_reg(dev, OMAP_I2C_BUFSTAT_REG);
+ if (stat & OMAP_I2C_STAT_RRDY)
+ num_bytes = dev->fifo_size;
+ else
+ num_bytes = omap_i2c_read_reg(dev,
+ OMAP_I2C_BUFSTAT_REG);
}
while (num_bytes) {
num_bytes--;
}
} else {
if (stat & OMAP_I2C_STAT_RRDY)
- dev_err(dev->dev, "RRDY IRQ while no data "
- "requested\n");
+ dev_err(dev->dev,
+ "RRDY IRQ while no data"
+ " requested\n");
if (stat & OMAP_I2C_STAT_RDR)
- dev_err(dev->dev, "RDR IRQ while no data "
- "requested\n");
+ dev_err(dev->dev,
+ "RDR IRQ while no data"
+ " requested\n");
break;
}
}
- omap_i2c_ack_stat(dev, stat & (OMAP_I2C_STAT_RRDY | OMAP_I2C_STAT_RDR));
+ omap_i2c_ack_stat(dev,
+ stat & (OMAP_I2C_STAT_RRDY | OMAP_I2C_STAT_RDR));
continue;
}
if (stat & (OMAP_I2C_STAT_XRDY | OMAP_I2C_STAT_XDR)) {
u8 num_bytes = 1;
if (dev->fifo_size) {
- num_bytes = (stat & OMAP_I2C_STAT_XRDY) ? dev->fifo_size :
- omap_i2c_read_reg(dev, OMAP_I2C_BUFSTAT_REG);
+ if (stat & OMAP_I2C_STAT_XRDY)
+ num_bytes = dev->fifo_size;
+ else
+ num_bytes = omap_i2c_read_reg(dev,
+ OMAP_I2C_BUFSTAT_REG);
}
while (num_bytes) {
num_bytes--;
}
} else {
if (stat & OMAP_I2C_STAT_XRDY)
- dev_err(dev->dev, "XRDY IRQ while no "
- "data to send\n");
+ dev_err(dev->dev,
+ "XRDY IRQ while no "
+ "data to send\n");
if (stat & OMAP_I2C_STAT_XDR)
- dev_err(dev->dev, "XDR IRQ while no "
- "data to send\n");
+ dev_err(dev->dev,
+ "XDR IRQ while no "
+ "data to send\n");
break;
}
omap_i2c_write_reg(dev, OMAP_I2C_DATA_REG, w);
}
- omap_i2c_ack_stat(dev, stat & (OMAP_I2C_STAT_XRDY | OMAP_I2C_STAT_XDR));
+ omap_i2c_ack_stat(dev,
+ stat & (OMAP_I2C_STAT_XRDY | OMAP_I2C_STAT_XDR));
continue;
}
if (stat & OMAP_I2C_STAT_ROVR) {
struct omap_i2c_dev *dev;
struct i2c_adapter *adap;
struct resource *mem, *irq, *ioarea;
+ void *isr;
int r;
- u32 *speed = NULL;
+ u32 speed = 0;
/* NOTE: driver uses the static register mapping */
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
}
if (pdev->dev.platform_data != NULL)
- speed = (u32 *) pdev->dev.platform_data;
+ speed = *(u32 *)pdev->dev.platform_data;
else
- *speed = 100; /* Defualt speed */
+ speed = 100; /* Defualt speed */
- dev->speed = *speed;
+ dev->speed = speed;
+ dev->idle = 1;
dev->dev = &pdev->dev;
dev->irq = irq->start;
- dev->base = (void __iomem *) IO_ADDRESS(mem->start);
+ dev->base = ioremap(mem->start, mem->end - mem->start + 1);
+ if (!dev->base) {
+ r = -ENOMEM;
+ goto err_free_mem;
+ }
+
platform_set_drvdata(pdev, dev);
if ((r = omap_i2c_get_clocks(dev)) != 0)
- goto err_free_mem;
+ goto err_iounmap;
omap_i2c_unidle(dev);
- if (cpu_is_omap15xx())
- dev->rev1 = omap_i2c_read_reg(dev, OMAP_I2C_REV_REG) < 0x20;
+ dev->rev = omap_i2c_read_reg(dev, OMAP_I2C_REV_REG) & 0xff;
if (cpu_is_omap2430() || cpu_is_omap34xx()) {
+ u16 s;
+
/* Set up the fifo size - Get total size */
- dev->fifo_size = 0x8 <<
- ((omap_i2c_read_reg(dev, OMAP_I2C_BUFSTAT_REG) >> 14) & 0x3);
+ s = (omap_i2c_read_reg(dev, OMAP_I2C_BUFSTAT_REG) >> 14) & 0x3;
+ dev->fifo_size = 0x8 << s;
+
/*
- * Set up notification threshold as half the total available size
- * This is to ensure that we can handle the status on int call back
- * latencies
+ * Set up notification threshold as half the total available
+ * size. This is to ensure that we can handle the status on int
+ * call back latencies.
*/
dev->fifo_size = (dev->fifo_size / 2);
dev->b_hw = 1; /* Enable hardware fixes */
/* reset ASAP, clearing any IRQs */
omap_i2c_init(dev);
- r = request_irq(dev->irq, dev->rev1 ? omap_i2c_rev1_isr : omap_i2c_isr,
- 0, pdev->name, dev);
+ isr = (dev->rev < OMAP_I2C_REV_2) ? omap_i2c_rev1_isr : omap_i2c_isr;
+ r = request_irq(dev->irq, isr, 0, pdev->name, dev);
if (r) {
dev_err(dev->dev, "failure requesting irq %i\n", dev->irq);
goto err_unuse_clocks;
}
- r = omap_i2c_read_reg(dev, OMAP_I2C_REV_REG) & 0xff;
+
dev_info(dev->dev, "bus %d rev%d.%d at %d kHz\n",
- pdev->id, r >> 4, r & 0xf, dev->speed);
+ pdev->id, dev->rev >> 4, dev->rev & 0xf, dev->speed);
+
+ omap_i2c_idle(dev);
adap = &dev->adapter;
i2c_set_adapdata(adap, dev);
goto err_free_irq;
}
- omap_i2c_idle(dev);
-
return 0;
err_free_irq:
omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, 0);
omap_i2c_idle(dev);
omap_i2c_put_clocks(dev);
+err_iounmap:
+ iounmap(dev->base);
err_free_mem:
platform_set_drvdata(pdev, NULL);
kfree(dev);
i2c_del_adapter(&dev->adapter);
omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, 0);
omap_i2c_put_clocks(dev);
+ iounmap(dev->base);
kfree(dev);
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
release_mem_region(mem->start, (mem->end - mem->start) + 1);
};
/* I2C may be needed to bring up other drivers */
-static int __devinit
+static int __init
omap_i2c_init_driver(void)
{
return platform_driver_register(&omap_i2c_driver);
}
subsys_initcall(omap_i2c_init_driver);
-static void __devexit omap_i2c_exit_driver(void)
+static void __exit omap_i2c_exit_driver(void)
{
platform_driver_unregister(&omap_i2c_driver);
}