omap: i2c: add a timeout to the busy waiting
[pandora-kernel.git] / drivers / i2c / busses / i2c-omap.c
index 389ac60..00fd02e 100644 (file)
@@ -38,6 +38,7 @@
 #include <linux/clk.h>
 #include <linux/io.h>
 #include <linux/slab.h>
+#include <linux/i2c-omap.h>
 
 /* I2C controller revisions */
 #define OMAP_I2C_REV_2                 0x20
 /* I2C controller revisions present on specific hardware */
 #define OMAP_I2C_REV_ON_2430           0x36
 #define OMAP_I2C_REV_ON_3430           0x3C
+#define OMAP_I2C_REV_ON_4430           0x40
 
 /* timeout waiting for the controller to respond */
 #define OMAP_I2C_TIMEOUT (msecs_to_jiffies(1000))
 
-#define OMAP_I2C_REV_REG               0x00
-#define OMAP_I2C_IE_REG                        0x01
-#define OMAP_I2C_STAT_REG              0x02
-#define OMAP_I2C_IV_REG                        0x03
 /* For OMAP3 I2C_IV has changed to I2C_WE (wakeup enable) */
-#define OMAP_I2C_WE_REG                        0x03
-#define OMAP_I2C_SYSS_REG              0x04
-#define OMAP_I2C_BUF_REG               0x05
-#define OMAP_I2C_CNT_REG               0x06
-#define OMAP_I2C_DATA_REG              0x07
-#define OMAP_I2C_SYSC_REG              0x08
-#define OMAP_I2C_CON_REG               0x09
-#define OMAP_I2C_OA_REG                        0x0a
-#define OMAP_I2C_SA_REG                        0x0b
-#define OMAP_I2C_PSC_REG               0x0c
-#define OMAP_I2C_SCLL_REG              0x0d
-#define OMAP_I2C_SCLH_REG              0x0e
-#define OMAP_I2C_SYSTEST_REG           0x0f
-#define OMAP_I2C_BUFSTAT_REG           0x10
+enum {
+       OMAP_I2C_REV_REG = 0,
+       OMAP_I2C_IE_REG,
+       OMAP_I2C_STAT_REG,
+       OMAP_I2C_IV_REG,
+       OMAP_I2C_WE_REG,
+       OMAP_I2C_SYSS_REG,
+       OMAP_I2C_BUF_REG,
+       OMAP_I2C_CNT_REG,
+       OMAP_I2C_DATA_REG,
+       OMAP_I2C_SYSC_REG,
+       OMAP_I2C_CON_REG,
+       OMAP_I2C_OA_REG,
+       OMAP_I2C_SA_REG,
+       OMAP_I2C_PSC_REG,
+       OMAP_I2C_SCLL_REG,
+       OMAP_I2C_SCLH_REG,
+       OMAP_I2C_SYSTEST_REG,
+       OMAP_I2C_BUFSTAT_REG,
+       OMAP_I2C_REVNB_LO,
+       OMAP_I2C_REVNB_HI,
+       OMAP_I2C_IRQSTATUS_RAW,
+       OMAP_I2C_IRQENABLE_SET,
+       OMAP_I2C_IRQENABLE_CLR,
+};
 
 /* I2C Interrupt Enable Register (OMAP_I2C_IE): */
 #define OMAP_I2C_IE_XDR                (1 << 14)       /* TX Buffer drain int enable */
@@ -167,9 +176,13 @@ struct omap_i2c_dev {
        struct clk              *fclk;          /* Functional clock */
        struct completion       cmd_complete;
        struct resource         *ioarea;
+       u32                     latency;        /* maximum mpu wkup latency */
+       void                    (*set_mpu_wkup_lat)(struct device *dev,
+                                                   long latency);
        u32                     speed;          /* Speed of bus in Khz */
        u16                     cmd_err;
        u8                      *buf;
+       u8                      *regs;
        size_t                  buf_len;
        struct i2c_adapter      adapter;
        u8                      fifo_size;      /* use as flag and value
@@ -188,15 +201,64 @@ struct omap_i2c_dev {
        u16                     westate;
 };
 
+const static u8 reg_map[] = {
+       [OMAP_I2C_REV_REG] = 0x00,
+       [OMAP_I2C_IE_REG] = 0x01,
+       [OMAP_I2C_STAT_REG] = 0x02,
+       [OMAP_I2C_IV_REG] = 0x03,
+       [OMAP_I2C_WE_REG] = 0x03,
+       [OMAP_I2C_SYSS_REG] = 0x04,
+       [OMAP_I2C_BUF_REG] = 0x05,
+       [OMAP_I2C_CNT_REG] = 0x06,
+       [OMAP_I2C_DATA_REG] = 0x07,
+       [OMAP_I2C_SYSC_REG] = 0x08,
+       [OMAP_I2C_CON_REG] = 0x09,
+       [OMAP_I2C_OA_REG] = 0x0a,
+       [OMAP_I2C_SA_REG] = 0x0b,
+       [OMAP_I2C_PSC_REG] = 0x0c,
+       [OMAP_I2C_SCLL_REG] = 0x0d,
+       [OMAP_I2C_SCLH_REG] = 0x0e,
+       [OMAP_I2C_SYSTEST_REG] = 0x0f,
+       [OMAP_I2C_BUFSTAT_REG] = 0x10,
+};
+
+const static u8 omap4_reg_map[] = {
+       [OMAP_I2C_REV_REG] = 0x04,
+       [OMAP_I2C_IE_REG] = 0x2c,
+       [OMAP_I2C_STAT_REG] = 0x28,
+       [OMAP_I2C_IV_REG] = 0x34,
+       [OMAP_I2C_WE_REG] = 0x34,
+       [OMAP_I2C_SYSS_REG] = 0x90,
+       [OMAP_I2C_BUF_REG] = 0x94,
+       [OMAP_I2C_CNT_REG] = 0x98,
+       [OMAP_I2C_DATA_REG] = 0x9c,
+       [OMAP_I2C_SYSC_REG] = 0x20,
+       [OMAP_I2C_CON_REG] = 0xa4,
+       [OMAP_I2C_OA_REG] = 0xa8,
+       [OMAP_I2C_SA_REG] = 0xac,
+       [OMAP_I2C_PSC_REG] = 0xb0,
+       [OMAP_I2C_SCLL_REG] = 0xb4,
+       [OMAP_I2C_SCLH_REG] = 0xb8,
+       [OMAP_I2C_SYSTEST_REG] = 0xbC,
+       [OMAP_I2C_BUFSTAT_REG] = 0xc0,
+       [OMAP_I2C_REVNB_LO] = 0x00,
+       [OMAP_I2C_REVNB_HI] = 0x04,
+       [OMAP_I2C_IRQSTATUS_RAW] = 0x24,
+       [OMAP_I2C_IRQENABLE_SET] = 0x2c,
+       [OMAP_I2C_IRQENABLE_CLR] = 0x30,
+};
+
 static inline void omap_i2c_write_reg(struct omap_i2c_dev *i2c_dev,
                                      int reg, u16 val)
 {
-       __raw_writew(val, i2c_dev->base + (reg << i2c_dev->reg_shift));
+       __raw_writew(val, i2c_dev->base +
+                       (i2c_dev->regs[reg] << i2c_dev->reg_shift));
 }
 
 static inline u16 omap_i2c_read_reg(struct omap_i2c_dev *i2c_dev, int reg)
 {
-       return __raw_readw(i2c_dev->base + (reg << i2c_dev->reg_shift));
+       return __raw_readw(i2c_dev->base +
+                               (i2c_dev->regs[reg] << i2c_dev->reg_shift));
 }
 
 static int __init omap_i2c_get_clocks(struct omap_i2c_dev *dev)
@@ -265,7 +327,11 @@ static void omap_i2c_idle(struct omap_i2c_dev *dev)
        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->rev >= OMAP_I2C_REV_ON_4430)
+               omap_i2c_write_reg(dev, OMAP_I2C_IRQENABLE_CLR, 1);
+       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 {
@@ -330,7 +396,9 @@ static int omap_i2c_init(struct omap_i2c_dev *dev)
                         * REVISIT: Some wkup sources might not be needed.
                         */
                        dev->westate = OMAP_I2C_WE_ALL;
-                       omap_i2c_write_reg(dev, OMAP_I2C_WE_REG, dev->westate);
+                       if (dev->rev < OMAP_I2C_REV_ON_4430)
+                               omap_i2c_write_reg(dev, OMAP_I2C_WE_REG,
+                                                               dev->westate);
                }
        }
        omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, 0);
@@ -357,7 +425,7 @@ static int omap_i2c_init(struct omap_i2c_dev *dev)
                        psc = fclk_rate / 12000000;
        }
 
-       if (cpu_is_omap2430() || cpu_is_omap34xx()) {
+       if (!(cpu_class_is_omap1() || cpu_is_omap2420())) {
 
                /*
                 * HSI2C controller internal clk rate should be 19.2 Mhz for
@@ -539,8 +607,12 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adap,
         * REVISIT: We should abort the transfer on signals, but the bus goes
         * into arbitration and we're currently unable to recover from it.
         */
+       if (dev->set_mpu_wkup_lat != NULL)
+               dev->set_mpu_wkup_lat(dev->dev, dev->latency);
        r = wait_for_completion_timeout(&dev->cmd_complete,
                                        OMAP_I2C_TIMEOUT);
+       if (dev->set_mpu_wkup_lat != NULL)
+               dev->set_mpu_wkup_lat(dev->dev, -1);
        dev->buf_len = 0;
        if (r < 0)
                return r;
@@ -684,6 +756,35 @@ omap_i2c_rev1_isr(int this_irq, void *dev_id)
 #define omap_i2c_rev1_isr              NULL
 #endif
 
+/*
+ * OMAP3430 Errata 1.153: When an XRDY/XDR is hit, wait for XUDF before writing
+ * data to DATA_REG. Otherwise some data bytes can be lost while transferring
+ * them from the memory to the I2C interface.
+ */
+static int errata_omap3_1p153(struct omap_i2c_dev *dev, u16 *stat, int *err)
+{
+       unsigned long timeout = 10000;
+
+       while (--timeout && !(*stat & OMAP_I2C_STAT_XUDF)) {
+               if (*stat & (OMAP_I2C_STAT_NACK | OMAP_I2C_STAT_AL)) {
+                       omap_i2c_ack_stat(dev, *stat & (OMAP_I2C_STAT_XRDY |
+                                                       OMAP_I2C_STAT_XDR));
+                       *err |= OMAP_I2C_STAT_XUDF;
+                       return -ETIMEDOUT;
+               }
+
+               cpu_relax();
+               *stat = omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG);
+       }
+
+       if (!timeout) {
+               dev_err(dev->dev, "timeout waiting on XUDF bit\n");
+               return 0;
+       }
+
+       return 0;
+}
+
 static irqreturn_t
 omap_i2c_isr(int this_irq, void *dev_id)
 {
@@ -747,9 +848,12 @@ complete:
                                if (dev->buf_len) {
                                        *dev->buf++ = w;
                                        dev->buf_len--;
-                                       /* Data reg from 2430 is 8 bit wide */
-                                       if (!cpu_is_omap2430() &&
-                                                       !cpu_is_omap34xx()) {
+                                       /*
+                                        * Data reg in 2430, omap3 and
+                                        * omap4 is 8 bit wide
+                                        */
+                                       if (cpu_class_is_omap1() ||
+                                                       cpu_is_omap2420()) {
                                                if (dev->buf_len) {
                                                        *dev->buf++ = w >> 8;
                                                        dev->buf_len--;
@@ -787,9 +891,12 @@ complete:
                                if (dev->buf_len) {
                                        w = *dev->buf++;
                                        dev->buf_len--;
-                                       /* Data reg from  2430 is 8 bit wide */
-                                       if (!cpu_is_omap2430() &&
-                                                       !cpu_is_omap34xx()) {
+                                       /*
+                                        * Data reg in 2430, omap3 and
+                                        * omap4 is 8 bit wide
+                                        */
+                                       if (cpu_class_is_omap1() ||
+                                                       cpu_is_omap2420()) {
                                                if (dev->buf_len) {
                                                        w |= *dev->buf++ << 8;
                                                        dev->buf_len--;
@@ -807,25 +914,9 @@ complete:
                                        break;
                                }
 
-                               /*
-                                * OMAP3430 Errata 1.153: When an XRDY/XDR
-                                * is hit, wait for XUDF before writing data
-                                * to DATA_REG. Otherwise some data bytes can
-                                * be lost while transferring them from the
-                                * memory to the I2C interface.
-                                */
-
-                               if (dev->rev <= OMAP_I2C_REV_ON_3430) {
-                                               while (!(stat & OMAP_I2C_STAT_XUDF)) {
-                                                       if (stat & (OMAP_I2C_STAT_NACK | OMAP_I2C_STAT_AL)) {
-                                                               omap_i2c_ack_stat(dev, stat & (OMAP_I2C_STAT_XRDY | OMAP_I2C_STAT_XDR));
-                                                               err |= OMAP_I2C_STAT_XUDF;
-                                                               goto complete;
-                                                       }
-                                                       cpu_relax();
-                                                       stat = omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG);
-                                               }
-                               }
+                               if ((dev->rev <= OMAP_I2C_REV_ON_3430) &&
+                                   errata_omap3_1p153(dev, &stat, &err))
+                                       goto complete;
 
                                omap_i2c_write_reg(dev, OMAP_I2C_DATA_REG, w);
                        }
@@ -857,6 +948,7 @@ omap_i2c_probe(struct platform_device *pdev)
        struct omap_i2c_dev     *dev;
        struct i2c_adapter      *adap;
        struct resource         *mem, *irq, *ioarea;
+       struct omap_i2c_bus_platform_data *pdata = pdev->dev.platform_data;
        irq_handler_t isr;
        int r;
        u32 speed = 0;
@@ -886,10 +978,13 @@ omap_i2c_probe(struct platform_device *pdev)
                goto err_release_region;
        }
 
-       if (pdev->dev.platform_data != NULL)
-               speed = *(u32 *)pdev->dev.platform_data;
-       else
-               speed = 100;    /* Defualt speed */
+       if (pdata != NULL) {
+               speed = pdata->clkrate;
+               dev->set_mpu_wkup_lat = pdata->set_mpu_wkup_lat;
+       } else {
+               speed = 100;    /* Default speed */
+               dev->set_mpu_wkup_lat = NULL;
+       }
 
        dev->speed = speed;
        dev->idle = 1;
@@ -905,17 +1000,24 @@ omap_i2c_probe(struct platform_device *pdev)
 
        if (cpu_is_omap7xx())
                dev->reg_shift = 1;
+       else if (cpu_is_omap44xx())
+               dev->reg_shift = 0;
        else
                dev->reg_shift = 2;
 
        if ((r = omap_i2c_get_clocks(dev)) != 0)
                goto err_iounmap;
 
+       if (cpu_is_omap44xx())
+               dev->regs = (u8 *) omap4_reg_map;
+       else
+               dev->regs = (u8 *) reg_map;
+
        omap_i2c_unidle(dev);
 
        dev->rev = omap_i2c_read_reg(dev, OMAP_I2C_REV_REG) & 0xff;
 
-       if (cpu_is_omap2430() || cpu_is_omap34xx()) {
+       if (!(cpu_class_is_omap1() || cpu_is_omap2420())) {
                u16 s;
 
                /* Set up the fifo size - Get total size */
@@ -927,8 +1029,17 @@ omap_i2c_probe(struct platform_device *pdev)
                 * 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 */
+               if (dev->rev >= OMAP_I2C_REV_ON_4430) {
+                       dev->fifo_size = 0;
+                       dev->b_hw = 0; /* Disable hardware fixes */
+               } else {
+                       dev->fifo_size = (dev->fifo_size / 2);
+                       dev->b_hw = 1; /* Enable hardware fixes */
+               }
+               /* calculate wakeup latency constraint for MPU */
+               if (dev->set_mpu_wkup_lat != NULL)
+                       dev->latency = (1000000 * dev->fifo_size) /
+                                      (1000 * speed / 8);
        }
 
        /* reset ASAP, clearing any IRQs */