Merge branch 'topic/soundcore-preclaim' into for-linus
[pandora-kernel.git] / drivers / i2c / busses / i2c-omap.c
index f2b82ee..827da08 100644 (file)
@@ -672,9 +672,17 @@ omap_i2c_isr(int this_irq, void *dev_id)
                        break;
                }
 
-               omap_i2c_write_reg(dev, OMAP_I2C_STAT_REG, stat);
-
                err = 0;
+complete:
+               /*
+                * Ack the stat in one go, but [R/X]DR and [R/X]RDY should be
+                * acked after the data operation is complete.
+                * Ref: TRM SWPU114Q Figure 18-31
+                */
+               omap_i2c_write_reg(dev, OMAP_I2C_STAT_REG, stat &
+                               ~(OMAP_I2C_STAT_RRDY | OMAP_I2C_STAT_RDR |
+                               OMAP_I2C_STAT_XRDY | OMAP_I2C_STAT_XDR));
+
                if (stat & OMAP_I2C_STAT_NACK) {
                        err |= OMAP_I2C_STAT_NACK;
                        omap_i2c_write_reg(dev, OMAP_I2C_CON_REG,
@@ -685,16 +693,22 @@ omap_i2c_isr(int this_irq, void *dev_id)
                        err |= OMAP_I2C_STAT_AL;
                }
                if (stat & (OMAP_I2C_STAT_ARDY | OMAP_I2C_STAT_NACK |
-                                       OMAP_I2C_STAT_AL))
+                                       OMAP_I2C_STAT_AL)) {
+                       omap_i2c_ack_stat(dev, stat &
+                               (OMAP_I2C_STAT_RRDY | OMAP_I2C_STAT_RDR |
+                               OMAP_I2C_STAT_XRDY | OMAP_I2C_STAT_XDR));
                        omap_i2c_complete_cmd(dev, err);
+                       return IRQ_HANDLED;
+               }
                if (stat & (OMAP_I2C_STAT_RRDY | OMAP_I2C_STAT_RDR)) {
                        u8 num_bytes = 1;
                        if (dev->fifo_size) {
                                if (stat & OMAP_I2C_STAT_RRDY)
                                        num_bytes = dev->fifo_size;
-                               else
-                                       num_bytes = omap_i2c_read_reg(dev,
-                                                       OMAP_I2C_BUFSTAT_REG);
+                               else    /* read RXSTAT on RDR interrupt */
+                                       num_bytes = (omap_i2c_read_reg(dev,
+                                                       OMAP_I2C_BUFSTAT_REG)
+                                                       >> 8) & 0x3F;
                        }
                        while (num_bytes) {
                                num_bytes--;
@@ -731,9 +745,10 @@ omap_i2c_isr(int this_irq, void *dev_id)
                        if (dev->fifo_size) {
                                if (stat & OMAP_I2C_STAT_XRDY)
                                        num_bytes = dev->fifo_size;
-                               else
+                               else    /* read TXSTAT on XDR interrupt */
                                        num_bytes = omap_i2c_read_reg(dev,
-                                                       OMAP_I2C_BUFSTAT_REG);
+                                                       OMAP_I2C_BUFSTAT_REG)
+                                                       & 0x3F;
                        }
                        while (num_bytes) {
                                num_bytes--;
@@ -760,6 +775,27 @@ omap_i2c_isr(int this_irq, void *dev_id)
                                                        "data to send\n");
                                        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);
+                                               }
+                               }
+
                                omap_i2c_write_reg(dev, OMAP_I2C_DATA_REG, w);
                        }
                        omap_i2c_ack_stat(dev,