]> pilppa.com Git - linux-2.6-omap-h63xx.git/commitdiff
FIFO Handling support and broken hw workaround
authorNishanth Menon <menon.nishanth@gmail.com>
Sat, 12 May 2007 00:36:02 +0000 (19:36 -0500)
committerTony Lindgren <tony@atomide.com>
Wed, 16 May 2007 22:03:57 +0000 (15:03 -0700)
*) xfers based can use FIFO on FIFO capable devices
*) Prevents errors for HSI2C if FIFO is not used
*) Should co-exist with no-FIFO support h/w also
*) Implemented errenous handling of STT-STP handling on SDP2430

Signed-off-by: Nishanth Menon <menon.nishanth@gmail.com>
Signed-off-by: Tony Lindgren <tony@atomide.com>
drivers/i2c/busses/i2c-omap.c

index fcc65929833ce32a32d78136411daebbcf605e5d..1441ac87dbc8d14ddb7151f65bc7e8a2ae682372 100644 (file)
 #define OMAP_I2C_SCLL_REG              0x34
 #define OMAP_I2C_SCLH_REG              0x38
 #define OMAP_I2C_SYSTEST_REG           0x3c
+#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_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 */
@@ -70,7 +73,8 @@
 #define OMAP_I2C_IE_AL         (1 << 0)        /* Arbitration lost int ena */
 
 /* I2C Status Register (OMAP_I2C_STAT): */
-#define OMAP_I2C_STAT_SBD      (1 << 15)       /* Single byte data */
+#define OMAP_I2C_STAT_XDR      (1 << 14)       /* TX Buffer draining */
+#define OMAP_I2C_STAT_RDR      (1 << 13)       /* RX Buffer draining */
 #define OMAP_I2C_STAT_BB       (1 << 12)       /* Bus busy */
 #define OMAP_I2C_STAT_ROVR     (1 << 11)       /* Receive overrun */
 #define OMAP_I2C_STAT_XUDF     (1 << 10)       /* Transmit underflow */
 
 /* I2C Buffer Configuration Register (OMAP_I2C_BUF): */
 #define OMAP_I2C_BUF_RDMA_EN   (1 << 15)       /* RX DMA channel enable */
+#define OMAP_I2C_BUF_RXFIF_CLR (1 << 14)       /* RX FIFO Clear */
 #define OMAP_I2C_BUF_XDMA_EN   (1 << 7)        /* TX DMA channel enable */
+#define OMAP_I2C_BUF_TXFIF_CLR (1 << 6)        /* TX FIFO Clear */
 
 /* I2C Configuration Register (OMAP_I2C_CON): */
 #define OMAP_I2C_CON_EN                (1 << 15)       /* I2C module enable */
 #define OMAP_I2C_CON_BE                (1 << 14)       /* Big endian mode */
-#define OMAP_I2C_CON_OPMODE    (1 << 12)       /* High Speed support */
+#define OMAP_I2C_CON_OPMODE_HS (1 << 12)       /* High Speed support */
 #define OMAP_I2C_CON_STB       (1 << 11)       /* Start byte mode (master) */
 #define OMAP_I2C_CON_MST       (1 << 10)       /* Master/slave mode */
 #define OMAP_I2C_CON_TRX       (1 << 9)        /* TX/RX mode (master only) */
@@ -133,7 +139,12 @@ struct omap_i2c_dev {
        u8                      *buf;
        size_t                  buf_len;
        struct i2c_adapter      adapter;
+       u8                      fifo_size;      /* use as flag and value
+                                                * fifo_size==0 implies no fifo
+                                                * if set, should be trsh+1
+                                                */
        unsigned                rev1:1;
+       unsigned                b_hw:1;         /* bad h/w fixes */
 };
 
 static inline void omap_i2c_write_reg(struct omap_i2c_dev *i2c_dev,
@@ -296,6 +307,14 @@ static int omap_i2c_init(struct omap_i2c_dev *dev)
        omap_i2c_write_reg(dev, OMAP_I2C_SCLL_REG, scll);
        omap_i2c_write_reg(dev, OMAP_I2C_SCLH_REG, sclh);
 
+       if (dev->fifo_size)
+               /* Note: setup required fifo size - 1 */
+               omap_i2c_write_reg(dev, OMAP_I2C_BUF_REG,
+                                       (dev->fifo_size - 1) << 8 | /* RTRSH */
+                                       OMAP_I2C_BUF_RXFIF_CLR |
+                                       (dev->fifo_size - 1) | /* XTRSH */
+                                       OMAP_I2C_BUF_TXFIF_CLR);
+
        /* Take the I2C module out of reset: */
        omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, OMAP_I2C_CON_EN);
 
@@ -303,7 +322,8 @@ static int omap_i2c_init(struct omap_i2c_dev *dev)
        omap_i2c_write_reg(dev, OMAP_I2C_IE_REG,
                        (OMAP_I2C_IE_XRDY | OMAP_I2C_IE_RRDY |
                        OMAP_I2C_IE_ARDY | OMAP_I2C_IE_NACK |
-                       OMAP_I2C_IE_AL));
+                       OMAP_I2C_IE_AL)  | ((dev->fifo_size) ?
+                               (OMAP_I2C_IE_RDR | OMAP_I2C_IE_XDR) : 0));
        return 0;
 }
 
@@ -375,6 +395,11 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adap,
 
        omap_i2c_write_reg(dev, OMAP_I2C_CNT_REG, dev->buf_len);
 
+       /* Clear the FIFO Buffers */
+       w = omap_i2c_read_reg(dev, OMAP_I2C_BUF_REG);
+       w |= OMAP_I2C_BUF_RXFIF_CLR | OMAP_I2C_BUF_TXFIF_CLR;
+       omap_i2c_write_reg(dev, OMAP_I2C_BUF_REG, w);
+
        init_completion(&dev->cmd_complete);
        dev->cmd_err = 0;
 
@@ -382,16 +407,27 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adap,
 
        /* High speed configuration */
        if (dev->speed > 400)
-               w |= OMAP_I2C_CON_OPMODE;
+               w |= OMAP_I2C_CON_OPMODE_HS;
 
        if (msg->flags & I2C_M_TEN)
                w |= OMAP_I2C_CON_XA;
        if (!(msg->flags & I2C_M_RD))
                w |= OMAP_I2C_CON_TRX;
-       if (stop)
+
+       if (!dev->b_hw && stop)
                w |= OMAP_I2C_CON_STP;
+
        omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, w);
 
+       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*/
+               }
+               w |= OMAP_I2C_CON_STP;
+               w &= ~OMAP_I2C_CON_STT;
+               omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, w);
+       }
        r = wait_for_completion_timeout(&dev->cmd_complete,
                                        OMAP_I2C_TIMEOUT);
        dev->buf_len = 0;
@@ -555,45 +591,73 @@ omap_i2c_isr(int this_irq, void *dev_id)
                        omap_i2c_complete_cmd(dev, 0);
                        continue;
                }
-               if (stat & OMAP_I2C_STAT_RRDY) {
-                       w = omap_i2c_read_reg(dev, OMAP_I2C_DATA_REG);
-                       if (dev->buf_len) {
-                               *dev->buf++ = w;
-                               dev->buf_len--;
-                               /*
-                                * Data reg in 2430 is 8 bit wide,
-                                */
-                               if (!cpu_is_omap2430()) {
-                                       if (dev->buf_len) {
-                                               *dev->buf++ = w >> 8;
-                                               dev->buf_len--;
+               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);
+                       }
+                       while (num_bytes) {
+                               num_bytes--;
+                               w = omap_i2c_read_reg(dev, OMAP_I2C_DATA_REG);
+                               if (dev->buf_len) {
+                                       *dev->buf++ = w;
+                                       dev->buf_len--;
+                                       /*
+                                        * Data reg in 2430 is 8 bit wide,
+                                        */
+                                       if (!cpu_is_omap2430()) {
+                                               if (dev->buf_len) {
+                                                       *dev->buf++ = w >> 8;
+                                                       dev->buf_len--;
+                                               }
                                        }
+                               } else {
+                                       if (stat & OMAP_I2C_STAT_RRDY)
+                                               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");
+                                       break;
                                }
-                       } else
-                               dev_err(dev->dev, "RRDY IRQ while no data"
-                                               "requested\n");
-                       omap_i2c_ack_stat(dev, OMAP_I2C_STAT_RRDY);
+                       }
+                       omap_i2c_ack_stat(dev, stat & (OMAP_I2C_STAT_RRDY | OMAP_I2C_STAT_RDR));
                        continue;
                }
-               if (stat & OMAP_I2C_STAT_XRDY) {
-                       w = 0;
-                       if (dev->buf_len) {
-                               w = *dev->buf++;
-                               dev->buf_len--;
-                               /*
-                                * Data reg in 2430 is 8 bit wide,
-                                */
-                               if (!cpu_is_omap2430()) {
-                                       if (dev->buf_len) {
-                                               w |= *dev->buf++ << 8;
-                                               dev->buf_len--;
+               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);
+                       }
+                       while (num_bytes) {
+                               num_bytes--;
+                               w = 0;
+                               if (dev->buf_len) {
+                                       w = *dev->buf++;
+                                       dev->buf_len--;
+                                       /*
+                                        * Data reg in 2430 is 8 bit wide,
+                                        */
+                                       if (!cpu_is_omap2430()) {
+                                               if (dev->buf_len) {
+                                                       w |= *dev->buf++ << 8;
+                                                       dev->buf_len--;
+                                               }
                                        }
+                               } else {
+                                       if (stat & OMAP_I2C_STAT_XRDY)
+                                               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");
+                                       break;
                                }
-                       } else
-                               dev_err(dev->dev, "XRDY IRQ while no"
-                                       "data to send\n");
-                       omap_i2c_write_reg(dev, OMAP_I2C_DATA_REG, w);
-                       omap_i2c_ack_stat(dev, OMAP_I2C_STAT_XRDY);
+                               omap_i2c_write_reg(dev, OMAP_I2C_DATA_REG, w);
+                       }
+                       omap_i2c_ack_stat(dev, stat & (OMAP_I2C_STAT_XRDY | OMAP_I2C_STAT_XDR));
                        continue;
                }
                if (stat & OMAP_I2C_STAT_ROVR) {
@@ -676,6 +740,19 @@ omap_i2c_probe(struct platform_device *pdev)
        if (cpu_is_omap15xx())
                dev->rev1 = omap_i2c_read_reg(dev, OMAP_I2C_REV_REG) < 0x20;
 
+       if (cpu_is_omap2430()) {
+               /* Set up the fifo size - Get total size */
+               dev->fifo_size = 0x8 <<
+                       ((omap_i2c_read_reg(dev, OMAP_I2C_BUFSTAT_REG) >> 14) & 0x3);
+               /*
+                * 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);