]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
i2c: bfin-twi: Read and write the FIFO in loop
authorSonic Zhang <sonic.zhang@analog.com>
Tue, 28 May 2013 10:41:09 +0000 (18:41 +0800)
committerWolfram Sang <wsa@the-dreams.de>
Wed, 12 Jun 2013 18:39:07 +0000 (20:39 +0200)
TWI transfer interrupts may be lost when system is heavily handling other
interrupts, while current transfer handler depends on each accurate interrupt
and misses some data in this case. Because there are 2 2-byte FIFOs in blackfin
TWI controller, the occurrence of the data loss can be reduced by reading till
the RX FIFO is empty and writing till the TX FIFO is full.

Reported-by: Bob Maris <mail@maris-ee.eu>
Signed-off-by: Sonic Zhang <sonic.zhang@analog.com>
Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
drivers/i2c/busses/i2c-bfin-twi.c

index 05080c449c6b9d374ca1e32a8510b1a9e24d902b..13ea1c29873d61f4028f48b7d8f49d7fb9a1afaa 100644 (file)
@@ -39,33 +39,40 @@ static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface,
        unsigned short mast_stat = read_MASTER_STAT(iface);
 
        if (twi_int_status & XMTSERV) {
+               if (iface->writeNum <= 0) {
+                       /* start receive immediately after complete sending in
+                        * combine mode.
+                        */
+                       if (iface->cur_mode == TWI_I2C_MODE_COMBINED)
+                               write_MASTER_CTL(iface,
+                                       read_MASTER_CTL(iface) | MDIR);
+                       else if (iface->manual_stop)
+                               write_MASTER_CTL(iface,
+                                       read_MASTER_CTL(iface) | STOP);
+                       else if (iface->cur_mode == TWI_I2C_MODE_REPEAT &&
+                               iface->cur_msg + 1 < iface->msg_num) {
+                               if (iface->pmsg[iface->cur_msg + 1].flags &
+                                       I2C_M_RD)
+                                       write_MASTER_CTL(iface,
+                                               read_MASTER_CTL(iface) |
+                                               MDIR);
+                               else
+                                       write_MASTER_CTL(iface,
+                                               read_MASTER_CTL(iface) &
+                                               ~MDIR);
+                       }
+               }
                /* Transmit next data */
-               if (iface->writeNum > 0) {
+               while (iface->writeNum > 0 &&
+                       (read_FIFO_STAT(iface) & XMTSTAT) != XMT_FULL) {
                        SSYNC();
                        write_XMT_DATA8(iface, *(iface->transPtr++));
                        iface->writeNum--;
                }
-               /* start receive immediately after complete sending in
-                * combine mode.
-                */
-               else if (iface->cur_mode == TWI_I2C_MODE_COMBINED)
-                       write_MASTER_CTL(iface,
-                               read_MASTER_CTL(iface) | MDIR);
-               else if (iface->manual_stop)
-                       write_MASTER_CTL(iface,
-                               read_MASTER_CTL(iface) | STOP);
-               else if (iface->cur_mode == TWI_I2C_MODE_REPEAT &&
-                        iface->cur_msg + 1 < iface->msg_num) {
-                       if (iface->pmsg[iface->cur_msg + 1].flags & I2C_M_RD)
-                               write_MASTER_CTL(iface,
-                                       read_MASTER_CTL(iface) | MDIR);
-                       else
-                               write_MASTER_CTL(iface,
-                                       read_MASTER_CTL(iface) & ~MDIR);
-               }
        }
        if (twi_int_status & RCVSERV) {
-               if (iface->readNum > 0) {
+               while (iface->readNum > 0 &&
+                       (read_FIFO_STAT(iface) & RCVSTAT)) {
                        /* Receive next data */
                        *(iface->transPtr) = read_RCV_DATA8(iface);
                        if (iface->cur_mode == TWI_I2C_MODE_COMBINED) {