i2c-bfin-twi: integrate timeout timer with completion interface
authorSonic Zhang <sonic.zhang@analog.com>
Mon, 22 Mar 2010 07:23:16 +0000 (03:23 -0400)
committerBen Dooks <ben-linux@fluff.org>
Wed, 19 May 2010 23:18:58 +0000 (00:18 +0100)
There isn't much point in managing our own custom timeout timer when the
completion interface already includes support for it.  This makes the
resulting code much simpler and robust.

Signed-off-by: Sonic Zhang <sonic.zhang@analog.com>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
Signed-off-by: Ben Dooks <ben-linux@fluff.org>
drivers/i2c/busses/i2c-bfin-twi.c

index f1e14dd590c977fdbb7e128cd0dfc5285f5379dd..441134a1df92bf125564c44a760cc628cb54d826 100644 (file)
@@ -25,8 +25,6 @@
 #include <asm/portmux.h>
 #include <asm/irq.h>
 
-#define POLL_TIMEOUT       (2 * HZ)
-
 /* SMBus mode*/
 #define TWI_I2C_MODE_STANDARD          1
 #define TWI_I2C_MODE_STANDARDSUB       2
@@ -44,8 +42,6 @@ struct bfin_twi_iface {
        int                     cur_mode;
        int                     manual_stop;
        int                     result;
-       int                     timeout_count;
-       struct timer_list       timeout_timer;
        struct i2c_adapter      adap;
        struct completion       complete;
        struct i2c_msg          *pmsg;
@@ -169,16 +165,13 @@ static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface)
                        write_INT_MASK(iface, 0);
                        write_MASTER_CTL(iface, 0);
                        SSYNC();
-                       /* If it is a quick transfer, only address bug no data,
+                       /* If it is a quick transfer, only address without data,
                         * not an err, return 1.
+                        * If address is acknowledged return 1.
                         */
-                       if (iface->writeNum == 0 && (mast_stat & BUFRDERR))
+                       if ((iface->writeNum == 0 && (mast_stat & BUFRDERR))
+                               || !(mast_stat & ANAK))
                                iface->result = 1;
-                       /* If address not acknowledged return -1,
-                        * else return 0.
-                        */
-                       else if (!(mast_stat & ANAK))
-                               iface->result = 0;
                }
                complete(&iface->complete);
                return;
@@ -250,9 +243,9 @@ static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface)
                        write_INT_MASK(iface, 0);
                        write_MASTER_CTL(iface, 0);
                        SSYNC();
-                       complete(&iface->complete);
                }
        }
+       complete(&iface->complete);
 }
 
 /* Interrupt handler */
@@ -262,36 +255,15 @@ static irqreturn_t bfin_twi_interrupt_entry(int irq, void *dev_id)
        unsigned long flags;
 
        spin_lock_irqsave(&iface->lock, flags);
-       del_timer(&iface->timeout_timer);
        bfin_twi_handle_interrupt(iface);
        spin_unlock_irqrestore(&iface->lock, flags);
        return IRQ_HANDLED;
 }
 
-static void bfin_twi_timeout(unsigned long data)
-{
-       struct bfin_twi_iface *iface = (struct bfin_twi_iface *)data;
-       unsigned long flags;
-
-       spin_lock_irqsave(&iface->lock, flags);
-       bfin_twi_handle_interrupt(iface);
-       if (iface->result == 0) {
-               iface->timeout_count--;
-               if (iface->timeout_count > 0) {
-                       iface->timeout_timer.expires = jiffies + POLL_TIMEOUT;
-                       add_timer(&iface->timeout_timer);
-               } else {
-                       iface->result = -1;
-                       complete(&iface->complete);
-               }
-       }
-       spin_unlock_irqrestore(&iface->lock, flags);
-}
-
 /*
- * Generic i2c master transfer entrypoint
+ * One i2c master transfer
  */
-static int bfin_twi_master_xfer(struct i2c_adapter *adap,
+static int bfin_twi_do_master_xfer(struct i2c_adapter *adap,
                                struct i2c_msg *msgs, int num)
 {
        struct bfin_twi_iface *iface = adap->algo_data;
@@ -319,7 +291,6 @@ static int bfin_twi_master_xfer(struct i2c_adapter *adap,
        iface->transPtr = pmsg->buf;
        iface->writeNum = iface->readNum = pmsg->len;
        iface->result = 0;
-       iface->timeout_count = 10;
        init_completion(&(iface->complete));
        /* Set Transmit device address */
        write_MASTER_ADDR(iface, pmsg->addr);
@@ -358,30 +329,49 @@ static int bfin_twi_master_xfer(struct i2c_adapter *adap,
                iface->manual_stop = 1;
        }
 
-       iface->timeout_timer.expires = jiffies + POLL_TIMEOUT;
-       add_timer(&iface->timeout_timer);
-
        /* Master enable */
        write_MASTER_CTL(iface, read_MASTER_CTL(iface) | MEN |
                ((iface->read_write == I2C_SMBUS_READ) ? MDIR : 0) |
                ((CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ > 100) ? FAST : 0));
        SSYNC();
 
-       wait_for_completion(&iface->complete);
-
-       rc = iface->result;
+       while (!iface->result) {
+               if (!wait_for_completion_timeout(&iface->complete,
+                       adap->timeout)) {
+                       iface->result = -1;
+                       dev_err(&adap->dev, "master transfer timeout\n");
+               }
+       }
 
-       if (rc == 1)
-               return num;
+       if (iface->result == 1)
+               rc = iface->cur_msg + 1;
        else
-               return rc;
+               rc = iface->result;
+
+       return rc;
 }
 
 /*
- * SMBus type transfer entrypoint
+ * Generic i2c master transfer entrypoint
  */
+static int bfin_twi_master_xfer(struct i2c_adapter *adap,
+                               struct i2c_msg *msgs, int num)
+{
+       int i, ret = 0;
 
-int bfin_twi_smbus_xfer(struct i2c_adapter *adap, u16 addr,
+       for (i = 0; i < adap->retries; i++) {
+               ret = bfin_twi_do_master_xfer(adap, msgs, num);
+               if (ret > 0)
+                       break;
+       }
+
+       return ret;
+}
+
+/*
+ * One I2C SMBus transfer
+ */
+int bfin_twi_do_smbus_xfer(struct i2c_adapter *adap, u16 addr,
                        unsigned short flags, char read_write,
                        u8 command, int size, union i2c_smbus_data *data)
 {
@@ -469,7 +459,6 @@ int bfin_twi_smbus_xfer(struct i2c_adapter *adap, u16 addr,
        iface->manual_stop = 0;
        iface->read_write = read_write;
        iface->command = command;
-       iface->timeout_count = 10;
        init_completion(&(iface->complete));
 
        /* FIFO Initiation. Data in FIFO should be discarded before
@@ -486,9 +475,6 @@ int bfin_twi_smbus_xfer(struct i2c_adapter *adap, u16 addr,
        write_MASTER_ADDR(iface, addr);
        SSYNC();
 
-       iface->timeout_timer.expires = jiffies + POLL_TIMEOUT;
-       add_timer(&iface->timeout_timer);
-
        switch (iface->cur_mode) {
        case TWI_I2C_MODE_STANDARDSUB:
                write_XMT_DATA8(iface, iface->command);
@@ -550,10 +536,8 @@ int bfin_twi_smbus_xfer(struct i2c_adapter *adap, u16 addr,
                                else if (iface->readNum > 255) {
                                        write_MASTER_CTL(iface, 0xff << 6);
                                        iface->manual_stop = 1;
-                               } else {
-                                       del_timer(&iface->timeout_timer);
+                               } else
                                        break;
-                               }
                        }
                }
                write_INT_MASK(iface, MCOMP | MERR |
@@ -569,13 +553,38 @@ int bfin_twi_smbus_xfer(struct i2c_adapter *adap, u16 addr,
        }
        SSYNC();
 
-       wait_for_completion(&iface->complete);
+       while (!iface->result) {
+               if (!wait_for_completion_timeout(&iface->complete,
+                       adap->timeout)) {
+                       iface->result = -1;
+                       dev_err(&adap->dev, "smbus transfer timeout\n");
+               }
+       }
 
        rc = (iface->result >= 0) ? 0 : -1;
 
        return rc;
 }
 
+/*
+ * Generic I2C SMBus transfer entrypoint
+ */
+int bfin_twi_smbus_xfer(struct i2c_adapter *adap, u16 addr,
+                       unsigned short flags, char read_write,
+                       u8 command, int size, union i2c_smbus_data *data)
+{
+       int i, ret = 0;
+
+       for (i = 0; i < adap->retries; i++) {
+               ret = bfin_twi_do_smbus_xfer(adap, addr, flags,
+                       read_write, command, size, data);
+               if (ret == 0)
+                       break;
+       }
+
+       return ret;
+}
+
 /*
  * Return what the adapter supports
  */
@@ -667,10 +676,6 @@ static int i2c_bfin_twi_probe(struct platform_device *pdev)
                goto out_error_no_irq;
        }
 
-       init_timer(&(iface->timeout_timer));
-       iface->timeout_timer.function = bfin_twi_timeout;
-       iface->timeout_timer.data = (unsigned long)iface;
-
        p_adap = &iface->adap;
        p_adap->nr = pdev->id;
        strlcpy(p_adap->name, pdev->name, sizeof(p_adap->name));
@@ -678,6 +683,8 @@ static int i2c_bfin_twi_probe(struct platform_device *pdev)
        p_adap->algo_data = iface;
        p_adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
        p_adap->dev.parent = &pdev->dev;
+       p_adap->timeout = 5 * HZ;
+       p_adap->retries = 3;
 
        rc = peripheral_request_list(pin_req[pdev->id], "i2c-bfin-twi");
        if (rc) {