]> err.no Git - linux-2.6/commitdiff
i2c-bfin-twi: Add repeat start feature to avoid break of a bundle of i2c master xfer...
authorSonic Zhang <sonic.zhang@analog.com>
Tue, 22 Apr 2008 20:16:47 +0000 (22:16 +0200)
committerJean Delvare <khali@hyperion.delvare>
Tue, 22 Apr 2008 20:16:47 +0000 (22:16 +0200)
 - Create a new mode TWI_I2C_MODE_REPEAT.
 - No change to smbus operation.

Signed-off-by: Sonic Zhang <sonic.zhang@analog.com>
Signed-off-by: Bryan Wu <cooloney@kernel.org>
Signed-off-by: Jean Delvare <khali@linux-fr.org>
drivers/i2c/busses/i2c-bfin-twi.c

index 7dbdaeb707a99c0caf78290f4263f7ceb515a475..86956a8f0ae5324748239907c60f72c380304f2e 100644 (file)
 #define POLL_TIMEOUT       (2 * HZ)
 
 /* SMBus mode*/
-#define TWI_I2C_MODE_STANDARD          0x01
-#define TWI_I2C_MODE_STANDARDSUB       0x02
-#define TWI_I2C_MODE_COMBINED          0x04
+#define TWI_I2C_MODE_STANDARD          1
+#define TWI_I2C_MODE_STANDARDSUB       2
+#define TWI_I2C_MODE_COMBINED          3
+#define TWI_I2C_MODE_REPEAT            4
 
 struct bfin_twi_iface {
        int                     irq;
@@ -58,6 +59,9 @@ struct bfin_twi_iface {
        struct timer_list       timeout_timer;
        struct i2c_adapter      adap;
        struct completion       complete;
+       struct i2c_msg          *pmsg;
+       int                     msg_num;
+       int                     cur_msg;
 };
 
 static struct bfin_twi_iface twi_iface;
@@ -76,12 +80,16 @@ static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface)
                /* start receive immediately after complete sending in
                 * combine mode.
                 */
-               else if (iface->cur_mode == TWI_I2C_MODE_COMBINED) {
+               else if (iface->cur_mode == TWI_I2C_MODE_COMBINED)
                        bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL()
                                | MDIR | RSTART);
-               else if (iface->manual_stop)
+               else if (iface->manual_stop)
                        bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL()
                                | STOP);
+               else if (iface->cur_mode == TWI_I2C_MODE_REPEAT &&
+                               iface->cur_msg+1 < iface->msg_num)
+                       bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL()
+                               | RSTART);
                SSYNC();
                /* Clear status */
                bfin_write_TWI_INT_STAT(XMTSERV);
@@ -108,6 +116,11 @@ static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface)
                        bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL()
                                | STOP);
                        SSYNC();
+               } else if (iface->cur_mode == TWI_I2C_MODE_REPEAT &&
+                               iface->cur_msg+1 < iface->msg_num) {
+                       bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL()
+                               | RSTART);
+                       SSYNC();
                }
                /* Clear interrupt source */
                bfin_write_TWI_INT_STAT(RCVSERV);
@@ -119,7 +132,7 @@ static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface)
                bfin_write_TWI_MASTER_STAT(0x3e);
                bfin_write_TWI_MASTER_CTL(0);
                SSYNC();
-               iface->result = -1;
+               iface->result = -EIO;
                /* if both err and complete int stats are set, return proper
                 * results.
                 */
@@ -170,6 +183,42 @@ static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface)
                        bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL() |
                                MEN | MDIR);
                        SSYNC();
+               } else if (iface->cur_mode == TWI_I2C_MODE_REPEAT &&
+                               iface->cur_msg+1 < iface->msg_num) {
+                       iface->cur_msg++;
+                       iface->transPtr = iface->pmsg[iface->cur_msg].buf;
+                       iface->writeNum = iface->readNum =
+                               iface->pmsg[iface->cur_msg].len;
+                       /* Set Transmit device address */
+                       bfin_write_TWI_MASTER_ADDR(
+                               iface->pmsg[iface->cur_msg].addr);
+                       if (iface->pmsg[iface->cur_msg].flags & I2C_M_RD)
+                               iface->read_write = I2C_SMBUS_READ;
+                       else {
+                               iface->read_write = I2C_SMBUS_WRITE;
+                               /* Transmit first data */
+                               if (iface->writeNum > 0) {
+                                       bfin_write_TWI_XMT_DATA8(
+                                               *(iface->transPtr++));
+                                       iface->writeNum--;
+                                       SSYNC();
+                               }
+                       }
+
+                       if (iface->pmsg[iface->cur_msg].len <= 255)
+                               bfin_write_TWI_MASTER_CTL(
+                               iface->pmsg[iface->cur_msg].len << 6);
+                       else {
+                               bfin_write_TWI_MASTER_CTL(0xff << 6);
+                               iface->manual_stop = 1;
+                       }
+                       /* remove restart bit and enable master receive */
+                       bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL() &
+                               ~RSTART);
+                       bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL() |
+                               MEN | ((iface->read_write == I2C_SMBUS_READ) ?
+                               MDIR : 0));
+                       SSYNC();
                } else {
                        iface->result = 1;
                        bfin_write_TWI_INT_MASK(0);
@@ -221,7 +270,6 @@ static int bfin_twi_master_xfer(struct i2c_adapter *adap,
 {
        struct bfin_twi_iface *iface = adap->algo_data;
        struct i2c_msg *pmsg;
-       int i, ret;
        int rc = 0;
 
        if (!(bfin_read_TWI_CONTROL() & TWI_ENA))
@@ -231,81 +279,76 @@ static int bfin_twi_master_xfer(struct i2c_adapter *adap,
                yield();
        }
 
-       ret = 0;
-       for (i = 0; rc >= 0 && i < num; i++) {
-               pmsg = &msgs[i];
-               if (pmsg->flags & I2C_M_TEN) {
-                       dev_err(&(adap->dev), "i2c-bfin-twi: 10 bits addr "
-                               "not supported !\n");
-                       rc = -EINVAL;
-                       break;
-               }
+       iface->pmsg = msgs;
+       iface->msg_num = num;
+       iface->cur_msg = 0;
 
-               iface->cur_mode = TWI_I2C_MODE_STANDARD;
-               iface->manual_stop = 0;
-               iface->transPtr = pmsg->buf;
-               iface->writeNum = iface->readNum = pmsg->len;
-               iface->result = 0;
-               iface->timeout_count = 10;
-               /* Set Transmit device address */
-               bfin_write_TWI_MASTER_ADDR(pmsg->addr);
-
-               /* FIFO Initiation. Data in FIFO should be
-                *  discarded before start a new operation.
-                */
-               bfin_write_TWI_FIFO_CTL(0x3);
-               SSYNC();
-               bfin_write_TWI_FIFO_CTL(0);
-               SSYNC();
+       pmsg = &msgs[0];
+       if (pmsg->flags & I2C_M_TEN) {
+               dev_err(&adap->dev, "10 bits addr not supported!\n");
+               return -EINVAL;
+       }
 
-               if (pmsg->flags & I2C_M_RD)
-                       iface->read_write = I2C_SMBUS_READ;
-               else {
-                       iface->read_write = I2C_SMBUS_WRITE;
-                       /* Transmit first data */
-                       if (iface->writeNum > 0) {
-                               bfin_write_TWI_XMT_DATA8(*(iface->transPtr++));
-                               iface->writeNum--;
-                               SSYNC();
-                       }
+       iface->cur_mode = TWI_I2C_MODE_REPEAT;
+       iface->manual_stop = 0;
+       iface->transPtr = pmsg->buf;
+       iface->writeNum = iface->readNum = pmsg->len;
+       iface->result = 0;
+       iface->timeout_count = 10;
+       /* Set Transmit device address */
+       bfin_write_TWI_MASTER_ADDR(pmsg->addr);
+
+       /* FIFO Initiation. Data in FIFO should be
+        *  discarded before start a new operation.
+        */
+       bfin_write_TWI_FIFO_CTL(0x3);
+       SSYNC();
+       bfin_write_TWI_FIFO_CTL(0);
+       SSYNC();
+
+       if (pmsg->flags & I2C_M_RD)
+               iface->read_write = I2C_SMBUS_READ;
+       else {
+               iface->read_write = I2C_SMBUS_WRITE;
+               /* Transmit first data */
+               if (iface->writeNum > 0) {
+                       bfin_write_TWI_XMT_DATA8(*(iface->transPtr++));
+                       iface->writeNum--;
+                       SSYNC();
                }
+       }
 
-               /* clear int stat */
-               bfin_write_TWI_INT_STAT(MERR|MCOMP|XMTSERV|RCVSERV);
+       /* clear int stat */
+       bfin_write_TWI_INT_STAT(MERR | MCOMP | XMTSERV | RCVSERV);
 
-               /* Interrupt mask . Enable XMT, RCV interrupt */
-               bfin_write_TWI_INT_MASK(MCOMP | MERR |
-                       ((iface->read_write == I2C_SMBUS_READ)?
-                       RCVSERV : XMTSERV));
-               SSYNC();
+       /* Interrupt mask . Enable XMT, RCV interrupt */
+       bfin_write_TWI_INT_MASK(MCOMP | MERR | RCVSERV | XMTSERV);
+       SSYNC();
 
-               if (pmsg->len > 0 && pmsg->len <= 255)
-                       bfin_write_TWI_MASTER_CTL(pmsg->len << 6);
-               else if (pmsg->len > 255) {
-                       bfin_write_TWI_MASTER_CTL(0xff << 6);
-                       iface->manual_stop = 1;
-               } else
-                       break;
+       if (pmsg->len <= 255)
+               bfin_write_TWI_MASTER_CTL(pmsg->len << 6);
+       else {
+               bfin_write_TWI_MASTER_CTL(0xff << 6);
+               iface->manual_stop = 1;
+       }
 
-               iface->timeout_timer.expires = jiffies + POLL_TIMEOUT;
-               add_timer(&iface->timeout_timer);
+       iface->timeout_timer.expires = jiffies + POLL_TIMEOUT;
+       add_timer(&iface->timeout_timer);
 
-               /* Master enable */
-               bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL() | MEN |
-                       ((iface->read_write == I2C_SMBUS_READ) ? MDIR : 0) |
-                       ((CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ>100) ? FAST : 0));
-               SSYNC();
+       /* Master enable */
+       bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL() | MEN |
+               ((iface->read_write == I2C_SMBUS_READ) ? MDIR : 0) |
+               ((CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ > 100) ? FAST : 0));
+       SSYNC();
 
-               wait_for_completion(&iface->complete);
+       wait_for_completion(&iface->complete);
 
-               rc = iface->result;
-               if (rc == 1)
-                       ret++;
-               else if (rc == -1)
-                       break;
-       }
+       rc = iface->result;
 
-       return ret;
+       if (rc == 1)
+               return num;
+       else
+               return rc;
 }
 
 /*