Remove I2C zero length transfer "HACK" and do real zero length transfers. This fixes Oops at kernel startup while "scanning" for TLV320AIC23IDx addresses.

Signed-off-by: Alexander Vasiliev <[EMAIL PROTECTED]>
Signed-off-by: Brad Griffis <[EMAIL PROTECTED]>
Signed-off-by: Dirk Behme <[EMAIL PROTECTED]>

Index: linux-davinci/drivers/i2c/busses/i2c-davinci.c
===================================================================
--- linux-davinci.orig/drivers/i2c/busses/i2c-davinci.c
+++ linux-davinci/drivers/i2c/busses/i2c-davinci.c
@@ -41,11 +41,6 @@
 
 #include <asm/arch/i2c.h>
 
-/* Hack to enable zero length transfers and smbus quick until clean fix
- * is available
- */
-#define DAVINCI_HACK
-
 /* ----- global defines ----------------------------------------------- */
 
 #define DAVINCI_I2C_TIMEOUT    (1*HZ)
@@ -118,6 +113,7 @@ struct davinci_i2c_dev {
        u8                      *buf;
        size_t                  buf_len;
        int                     irq;
+       int                     stop;
        u8                      terminate;
        struct i2c_adapter      adapter;
 };
@@ -249,14 +245,6 @@ i2c_davinci_xfer_msg(struct i2c_adapter 
        u32 flag;
        u16 w;
        int r;
-#ifdef DAVINCI_HACK
-       u8 zero_byte = 0;
-#endif
-
-#ifndef DAVINCI_HACK
-       if (msg->len == 0)
-               return -EINVAL;
-#endif
 
        if (!pdata)
                pdata = &davinci_i2c_platform_data_default;
@@ -267,18 +255,9 @@ i2c_davinci_xfer_msg(struct i2c_adapter 
        /* set the slave address */
        davinci_i2c_write_reg(dev, DAVINCI_I2C_SAR_REG, msg->addr);
 
-#ifndef DAVINCI_HACK
        dev->buf = msg->buf;
        dev->buf_len = msg->len;
-#else
-       if (msg->len == 0) {
-               dev->buf = &zero_byte;
-               dev->buf_len = 1;
-       } else {
-               dev->buf = msg->buf;
-               dev->buf_len = msg->len;
-       }
-#endif
+       dev->stop = stop;
 
        davinci_i2c_write_reg(dev, DAVINCI_I2C_CNT_REG, dev->buf_len);
 
@@ -296,6 +275,10 @@ i2c_davinci_xfer_msg(struct i2c_adapter 
                flag |= DAVINCI_I2C_MDR_TRX;
        if (stop)
                flag |= DAVINCI_I2C_MDR_STP;
+       if (msg->len == 0) {
+               flag |= DAVINCI_I2C_MDR_RM;
+               flag &= ~DAVINCI_I2C_MDR_STP;
+       }
 
        /* Enable receive or transmit interrupts */
        w = davinci_i2c_read_reg(dev, DAVINCI_I2C_IMR_REG);
@@ -306,6 +289,16 @@ i2c_davinci_xfer_msg(struct i2c_adapter 
        davinci_i2c_write_reg(dev, DAVINCI_I2C_IMR_REG, w);
 
        dev->terminate = 0;
+
+       /* First byte should be set here, not after interrupt,
+        * because transmit-data-ready interrupt can come before
+        * NACK-interrupt during sending of previous message and
+        * ICDXR may have wrong data */
+       if ((!(msg->flags & I2C_M_RD)) && dev->buf_len) {
+               davinci_i2c_write_reg(dev, DAVINCI_I2C_DXR_REG, *dev->buf++);
+               dev->buf_len--;
+       }
+
        /* write the data into mode register */
        davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, flag);
 
@@ -344,11 +337,6 @@ i2c_davinci_xfer_msg(struct i2c_adapter 
        if (dev->cmd_err & DAVINCI_I2C_STR_NACK) {
                if (msg->flags & I2C_M_IGNORE_NAK)
                        return msg->len;
-               if (stop) {
-                       w = davinci_i2c_read_reg(dev, DAVINCI_I2C_MDR_REG);
-                       MOD_REG_BIT(w, DAVINCI_I2C_MDR_STP, 1);
-                       davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, w);
-               }
                return -EREMOTEIO;
        }
        return -EIO;
@@ -383,11 +371,7 @@ i2c_davinci_xfer(struct i2c_adapter *ada
 
 static u32 i2c_davinci_func(struct i2c_adapter *adap)
 {
-#ifndef DAVINCI_HACK
-       return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
-#else
        return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
-#endif
 }
 
 static inline void terminate_read(struct davinci_i2c_dev *dev)
@@ -404,15 +388,6 @@ static inline void terminate_read(struct
        if (!dev->terminate)
                dev_err(dev->dev, "RDR IRQ while no data requested\n");
 }
-static inline void terminate_write(struct davinci_i2c_dev *dev)
-{
-       u16 w = davinci_i2c_read_reg(dev, DAVINCI_I2C_MDR_REG);
-       w |= DAVINCI_I2C_MDR_RM|DAVINCI_I2C_MDR_STP;
-       davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, w);
-
-       if (!dev->terminate)
-               dev_err(dev->dev, "TDR IRQ while no data to send\n");
-}
 
 /*
  * Interrupt service routine. This gets called whenever an I2C interrupt
@@ -449,6 +424,14 @@ static irqreturn_t i2c_davinci_isr(int t
                case DAVINCI_I2C_IVR_ARDY:
                        davinci_i2c_write_reg(dev,
                                DAVINCI_I2C_STR_REG, DAVINCI_I2C_STR_ARDY);
+                       if (((dev->buf_len == 0) && (dev->stop != 0)) ||
+                           (dev->cmd_err & DAVINCI_I2C_STR_NACK)) {
+                               w = davinci_i2c_read_reg(dev,
+                                                        DAVINCI_I2C_MDR_REG);
+                               MOD_REG_BIT(w, DAVINCI_I2C_MDR_STP, 1);
+                               davinci_i2c_write_reg(dev,
+                                                     DAVINCI_I2C_MDR_REG, w);
+                       }
                        complete(&dev->cmd_complete);
                        break;
 
@@ -484,9 +467,6 @@ static irqreturn_t i2c_davinci_isr(int t
                                davinci_i2c_write_reg(dev,
                                                      DAVINCI_I2C_IMR_REG,
                                                      w);
-                       } else {
-                               /* signal can terminate transfer */
-                               terminate_write(dev);
                        }
                        break;
 
_______________________________________________
Davinci-linux-open-source mailing list
[email protected]
http://linux.davincidsp.com/mailman/listinfo/davinci-linux-open-source

Reply via email to