diff options
Diffstat (limited to 'drivers/i2c/busses/i2c-omap.c')
-rw-r--r-- | drivers/i2c/busses/i2c-omap.c | 135 |
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; } |