aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/i2c/busses/i2c-omap.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/i2c/busses/i2c-omap.c')
-rw-r--r--drivers/i2c/busses/i2c-omap.c135
1 files changed, 79 insertions, 56 deletions
diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c
index 137e1a3..dbed621 100644
--- a/drivers/i2c/busses/i2c-omap.c
+++ b/drivers/i2c/busses/i2c-omap.c
@@ -40,6 +40,7 @@
#include <linux/slab.h>
#include <linux/i2c-omap.h>
#include <linux/pm_runtime.h>
+#include <linux/pm_qos_params.h>
/* I2C controller revisions */
#define OMAP_I2C_REV_2 0x20
@@ -75,6 +76,7 @@ enum {
OMAP_I2C_REVNB_LO,
OMAP_I2C_REVNB_HI,
OMAP_I2C_IRQSTATUS_RAW,
+ OMAP_I2C_IRQSTATUS,
OMAP_I2C_IRQENABLE_SET,
OMAP_I2C_IRQENABLE_CLR,
};
@@ -143,7 +145,6 @@ enum {
#define OMAP_I2C_SCLH_HSSCLH 8
/* I2C System Test Register (OMAP_I2C_SYSTEST): */
-#ifdef DEBUG
#define OMAP_I2C_SYSTEST_ST_EN (1 << 15) /* System test enable */
#define OMAP_I2C_SYSTEST_FREE (1 << 14) /* Free running mode */
#define OMAP_I2C_SYSTEST_TMODE_MASK (3 << 12) /* Test mode select */
@@ -152,7 +153,6 @@ enum {
#define OMAP_I2C_SYSTEST_SCL_O (1 << 2) /* SCL line drive out */
#define OMAP_I2C_SYSTEST_SDA_I (1 << 1) /* SDA line sense in */
#define OMAP_I2C_SYSTEST_SDA_O (1 << 0) /* SDA line drive out */
-#endif
/* OCP_SYSSTATUS bit definitions */
#define SYSS_RESETDONE_MASK (1 << 0)
@@ -179,8 +179,7 @@ struct omap_i2c_dev {
struct completion cmd_complete;
struct resource *ioarea;
u32 latency; /* maximum mpu wkup latency */
- void (*set_mpu_wkup_lat)(struct device *dev,
- long latency);
+ struct pm_qos_request_list *pm_qos;
u32 speed; /* Speed of bus in Khz */
u16 cmd_err;
u8 *buf;
@@ -247,6 +246,7 @@ const static u8 omap4_reg_map[] = {
[OMAP_I2C_REVNB_LO] = 0x00,
[OMAP_I2C_REVNB_HI] = 0x04,
[OMAP_I2C_IRQSTATUS_RAW] = 0x24,
+ [OMAP_I2C_IRQSTATUS] = 0x28,
[OMAP_I2C_IRQENABLE_SET] = 0x2c,
[OMAP_I2C_IRQENABLE_CLR] = 0x30,
};
@@ -276,7 +276,7 @@ static void omap_i2c_unidle(struct omap_i2c_dev *dev)
pm_runtime_get_sync(&pdev->dev);
- if (cpu_is_omap34xx()) {
+ if (cpu_is_omap34xx() || cpu_is_omap44xx()) {
omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, 0);
omap_i2c_write_reg(dev, OMAP_I2C_PSC_REG, dev->pscstate);
omap_i2c_write_reg(dev, OMAP_I2C_SCLL_REG, dev->scllstate);
@@ -288,12 +288,12 @@ static void omap_i2c_unidle(struct omap_i2c_dev *dev)
}
dev->idle = 0;
- /*
- * Don't write to this register if the IE state is 0 as it can
- * cause deadlock.
- */
- if (dev->iestate)
+ if (cpu_is_omap44xx() && dev->rev >= OMAP_I2C_REV_ON_4430) {
+ omap_i2c_write_reg(dev, OMAP_I2C_IRQENABLE_CLR,0x6FFF);
+ omap_i2c_write_reg(dev, OMAP_I2C_IRQENABLE_SET, dev->iestate);
+ } else {
omap_i2c_write_reg(dev, OMAP_I2C_IE_REG, dev->iestate);
+ }
}
static void omap_i2c_idle(struct omap_i2c_dev *dev)
@@ -307,19 +307,13 @@ static void omap_i2c_idle(struct omap_i2c_dev *dev)
pdev = to_platform_device(dev->dev);
pdata = pdev->dev.platform_data;
- dev->iestate = omap_i2c_read_reg(dev, OMAP_I2C_IE_REG);
- if (dev->rev >= OMAP_I2C_REV_ON_4430)
- omap_i2c_write_reg(dev, OMAP_I2C_IRQENABLE_CLR, 1);
+ if (cpu_is_omap44xx() && dev->rev >= OMAP_I2C_REV_ON_4430)
+ omap_i2c_write_reg(dev, OMAP_I2C_IRQENABLE_CLR, 0x6FFF);
else
omap_i2c_write_reg(dev, OMAP_I2C_IE_REG, 0);
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);
-
- /* Flush posted write before the dev->idle store occurs */
- omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG);
}
dev->idle = 1;
@@ -487,13 +481,7 @@ static int omap_i2c_init(struct omap_i2c_dev *dev)
if (cpu_is_omap2430() || cpu_is_omap34xx())
dev->errata |= I2C_OMAP_ERRATA_I207;
- /* Enable interrupts */
- dev->iestate = (OMAP_I2C_IE_XRDY | OMAP_I2C_IE_RRDY |
- OMAP_I2C_IE_ARDY | OMAP_I2C_IE_NACK |
- OMAP_I2C_IE_AL) | ((dev->fifo_size) ?
- (OMAP_I2C_IE_RDR | OMAP_I2C_IE_XDR) : 0);
- omap_i2c_write_reg(dev, OMAP_I2C_IE_REG, dev->iestate);
- if (cpu_is_omap34xx()) {
+ if (cpu_is_omap34xx() || cpu_is_omap44xx()) {
dev->pscstate = psc;
dev->scllstate = scll;
dev->sclhstate = sclh;
@@ -611,8 +599,7 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adap,
return 0;
/* We have an error */
- if (dev->cmd_err & (OMAP_I2C_STAT_AL | OMAP_I2C_STAT_ROVR |
- OMAP_I2C_STAT_XUDF)) {
+ if (dev->cmd_err & OMAP_I2C_STAT_AL) {
omap_i2c_init(dev);
return -EIO;
}
@@ -641,15 +628,34 @@ omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
struct omap_i2c_dev *dev = i2c_get_adapdata(adap);
int i;
int r;
+ u16 val;
omap_i2c_unidle(dev);
r = omap_i2c_wait_for_bb(dev);
+ /* If timeout, try to again check after soft reset of I2C block */
+ if (WARN_ON(r == -ETIMEDOUT)) {
+ /* Provide a permanent clock to recover the peripheral */
+ val = omap_i2c_read_reg(dev, OMAP_I2C_SYSTEST_REG);
+ val |= (OMAP_I2C_SYSTEST_ST_EN |
+ OMAP_I2C_SYSTEST_FREE |
+ (2 << OMAP_I2C_SYSTEST_TMODE_SHIFT));
+ omap_i2c_write_reg(dev, OMAP_I2C_SYSTEST_REG, val);
+ msleep(1);
+ omap_i2c_init(dev);
+ r = omap_i2c_wait_for_bb(dev);
+ }
if (r < 0)
goto out;
- if (dev->set_mpu_wkup_lat != NULL)
- dev->set_mpu_wkup_lat(dev->dev, dev->latency);
+ /*
+ * When waiting for completion of a i2c transfer, we need to
+ * set a wake up latency constraint for the MPU. This is to
+ * ensure quick enough wakeup from idle, when transfer
+ * completes.
+ */
+ if (dev->pm_qos)
+ pm_qos_update_request(dev->pm_qos, dev->latency);
for (i = 0; i < num; i++) {
r = omap_i2c_xfer_msg(adap, &msgs[i], (i == (num - 1)));
@@ -657,8 +663,8 @@ omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
break;
}
- if (dev->set_mpu_wkup_lat != NULL)
- dev->set_mpu_wkup_lat(dev->dev, -1);
+ if (dev->pm_qos)
+ pm_qos_update_request(dev->pm_qos, PM_QOS_DEFAULT_VALUE);
if (r == 0)
r = num;
@@ -810,15 +816,13 @@ static irqreturn_t
omap_i2c_isr(int this_irq, void *dev_id)
{
struct omap_i2c_dev *dev = dev_id;
- u16 bits;
u16 stat, w;
int err, count = 0;
if (dev->idle)
return IRQ_NONE;
- bits = omap_i2c_read_reg(dev, OMAP_I2C_IE_REG);
- while ((stat = (omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG))) & bits) {
+ while ((stat = (omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG))) & dev->iestate) {
dev_dbg(dev->dev, "IRQ (ISR = 0x%04x)\n", stat);
if (count++ == 100) {
dev_warn(dev->dev, "Too much work in one IRQ\n");
@@ -954,12 +958,10 @@ complete:
continue;
}
if (stat & OMAP_I2C_STAT_ROVR) {
- dev_err(dev->dev, "Receive overrun\n");
- dev->cmd_err |= OMAP_I2C_STAT_ROVR;
+ dev_dbg(dev->dev, "Receive overrun\n");
}
if (stat & OMAP_I2C_STAT_XUDF) {
- dev_err(dev->dev, "Transmit underflow\n");
- dev->cmd_err |= OMAP_I2C_STAT_XUDF;
+ dev_dbg(dev->dev, "Transmit underflow\n");
}
}
@@ -1007,13 +1009,10 @@ omap_i2c_probe(struct platform_device *pdev)
goto err_release_region;
}
- if (pdata != NULL) {
+ if (pdata)
speed = pdata->clkrate;
- dev->set_mpu_wkup_lat = pdata->set_mpu_wkup_lat;
- } else {
+ else
speed = 100; /* Default speed */
- dev->set_mpu_wkup_lat = NULL;
- }
dev->speed = speed;
dev->idle = 1;
@@ -1025,6 +1024,17 @@ omap_i2c_probe(struct platform_device *pdev)
goto err_free_mem;
}
+ if (pdata && pdata->needs_wakeup_latency) {
+ dev->pm_qos = kzalloc(sizeof(struct pm_qos_request_list),
+ GFP_KERNEL);
+ if (!dev->pm_qos) {
+ r = -ENOMEM;
+ goto err_unmap;
+ }
+ pm_qos_add_request(dev->pm_qos, PM_QOS_CPU_DMA_LATENCY,
+ PM_QOS_DEFAULT_VALUE);
+ }
+
platform_set_drvdata(pdev, dev);
if (cpu_is_omap7xx())
@@ -1040,7 +1050,8 @@ omap_i2c_probe(struct platform_device *pdev)
dev->regs = (u8 *) reg_map;
pm_runtime_enable(&pdev->dev);
- omap_i2c_unidle(dev);
+ pm_runtime_get_sync(&pdev->dev);
+ dev->idle = 0;
dev->rev = omap_i2c_read_reg(dev, OMAP_I2C_REV_REG) & 0xff;
@@ -1059,15 +1070,14 @@ omap_i2c_probe(struct platform_device *pdev)
* size. This is to ensure that we can handle the status on int
* call back latencies.
*/
- if (dev->rev >= OMAP_I2C_REV_ON_4430) {
- dev->fifo_size = 0;
+ dev->fifo_size = (dev->fifo_size / 2);
+ if (dev->rev >= OMAP_I2C_REV_ON_4430)
dev->b_hw = 0; /* Disable hardware fixes */
- } else {
- dev->fifo_size = (dev->fifo_size / 2);
+ else
dev->b_hw = 1; /* Enable hardware fixes */
- }
+
/* calculate wakeup latency constraint for MPU */
- if (dev->set_mpu_wkup_lat != NULL)
+ if (dev->pm_qos)
dev->latency = (1000000 * dev->fifo_size) /
(1000 * speed / 8);
}
@@ -1075,6 +1085,12 @@ omap_i2c_probe(struct platform_device *pdev)
/* reset ASAP, clearing any IRQs */
omap_i2c_init(dev);
+ /* Decide what interrupts are needed */
+ dev->iestate = (OMAP_I2C_IE_XRDY | OMAP_I2C_IE_RRDY |
+ OMAP_I2C_IE_ARDY | OMAP_I2C_IE_NACK |
+ OMAP_I2C_IE_AL) | ((dev->fifo_size) ?
+ (OMAP_I2C_IE_RDR | OMAP_I2C_IE_XDR) : 0);
+
isr = (dev->rev < OMAP_I2C_REV_2) ? omap_i2c_rev1_isr : omap_i2c_isr;
r = request_irq(dev->irq, isr, 0, pdev->name, dev);
@@ -1111,6 +1127,11 @@ err_free_irq:
err_unuse_clocks:
omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, 0);
omap_i2c_idle(dev);
+ if (dev->pm_qos) {
+ pm_qos_remove_request(dev->pm_qos);
+ kfree(dev->pm_qos);
+ }
+err_unmap:
iounmap(dev->base);
err_free_mem:
platform_set_drvdata(pdev, NULL);
@@ -1133,6 +1154,10 @@ omap_i2c_remove(struct platform_device *pdev)
i2c_del_adapter(&dev->adapter);
omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, 0);
iounmap(dev->base);
+ if (dev->pm_qos) {
+ pm_qos_remove_request(dev->pm_qos);
+ kfree(dev->pm_qos);
+ }
kfree(dev);
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
release_mem_region(mem->start, resource_size(mem));
@@ -1142,18 +1167,16 @@ omap_i2c_remove(struct platform_device *pdev)
#ifdef CONFIG_SUSPEND
static int omap_i2c_suspend(struct device *dev)
{
- if (!pm_runtime_suspended(dev))
- if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_suspend)
- dev->bus->pm->runtime_suspend(dev);
+ if (dev->power.runtime_auto == false)
+ pm_runtime_put_sync(dev);
return 0;
}
static int omap_i2c_resume(struct device *dev)
{
- if (!pm_runtime_suspended(dev))
- if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_resume)
- dev->bus->pm->runtime_resume(dev);
+ if (dev->power.runtime_auto == false)
+ pm_runtime_get_sync(dev);
return 0;
}