]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/i2c/busses/i2c-omap.c
i2c-omap: reprogram OCP_SYSCONFIG register after reset
[linux-2.6-omap-h63xx.git] / drivers / i2c / busses / i2c-omap.c
index 40a1e4bc92f189b8127f6ae38a4f90a12b83282f..b20235415878fa6f02fb13595f7c08b4f3f82ee7 100644 (file)
 #include <linux/clk.h>
 #include <linux/io.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
+
 /* timeout waiting for the controller to respond */
 #define OMAP_I2C_TIMEOUT (msecs_to_jiffies(1000))
 
 #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;
@@ -139,7 +154,7 @@ struct omap_i2c_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 */
@@ -209,7 +224,7 @@ static void omap_i2c_idle(struct omap_i2c_dev *dev)
 
        dev->iestate = omap_i2c_read_reg(dev, OMAP_I2C_IE_REG);
        omap_i2c_write_reg(dev, OMAP_I2C_IE_REG, 0);
-       if (dev->rev1) {
+       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);
@@ -231,14 +246,14 @@ static int omap_i2c_init(struct omap_i2c_dev *dev)
        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");
@@ -246,6 +261,26 @@ static int omap_i2c_init(struct omap_i2c_dev *dev)
                        }
                        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);
 
@@ -710,6 +745,7 @@ omap_i2c_probe(struct platform_device *pdev)
        struct omap_i2c_dev     *dev;
        struct i2c_adapter      *adap;
        struct resource         *mem, *irq, *ioarea;
+       void *isr;
        int r;
        u32 speed = 0;
 
@@ -760,8 +796,7 @@ omap_i2c_probe(struct platform_device *pdev)
 
        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;
@@ -782,16 +817,16 @@ omap_i2c_probe(struct platform_device *pdev)
        /* 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);