From 37aaf072da3697f832011c1f4d238b1356386d67 Mon Sep 17 00:00:00 2001
From: kfx <kfx@rock-chips.com>
Date: Sat, 28 Jul 2012 10:09:59 +0800
Subject: [PATCH] i2c: fix bug: 'the transmission may be abnormal, if send the
 stop signal immediately after timeout'

---
 arch/arm/mach-rk30/devices.c          | 162 ++++++++++++++++++++++++++
 arch/arm/plat-rk/include/plat/board.h |   7 ++
 drivers/i2c/busses/i2c-rk30-adapter.c |  93 ++++++++++-----
 drivers/i2c/busses/i2c-rk30.c         |   5 +-
 drivers/i2c/busses/i2c-rk30.h         |   3 +-
 drivers/i2c/i2c-core.c                |   2 +
 6 files changed, 238 insertions(+), 34 deletions(-)

diff --git a/arch/arm/mach-rk30/devices.c b/arch/arm/mach-rk30/devices.c
index 2a9838aa123b..c456492da10f 100755
--- a/arch/arm/mach-rk30/devices.c
+++ b/arch/arm/mach-rk30/devices.c
@@ -368,10 +368,42 @@ static void __init rk30_init_uart(void)
 #endif
 
 #ifdef CONFIG_I2C0_RK30
+static int i2c0_check_idle(void)
+{
+        int sda_level, scl_level;
+        rk30_mux_api_set(GPIO2D5_I2C0SCL_NAME, GPIO2D_GPIO2D5);	
+	rk30_mux_api_set(GPIO2D4_I2C0SDA_NAME, GPIO2D_GPIO2D4);
+
+        gpio_request(RK30_PIN2_PD5, "i2c.0");
+        gpio_request(RK30_PIN2_PD4, "i2c.0");
+        
+        gpio_direction_input(RK30_PIN2_PD5);
+        gpio_direction_input(RK30_PIN2_PD4);
+
+        scl_level = gpio_get_value(RK30_PIN2_PD5);
+        sda_level = gpio_get_value(RK30_PIN2_PD4);
+
+        gpio_free(RK30_PIN2_PD5);
+        gpio_free(RK30_PIN2_PD4);
+
+        rk30_mux_api_set(GPIO2D5_I2C0SCL_NAME, GPIO2D_I2C0_SCL);	
+	rk30_mux_api_set(GPIO2D4_I2C0SDA_NAME, GPIO2D_I2C0_SDA);
+
+        if(sda_level == 1 && scl_level == 1)
+                return I2C_IDLE;
+        else if(sda_level == 0 && scl_level == 1)
+                return I2C_SDA_LOW;
+        else if(sda_level == 1 && scl_level == 0)
+                return I2C_SCL_LOW;
+        else
+                return BOTH_LOW;
+}
+
 static struct rk30_i2c_platform_data default_i2c0_data = {
 	.bus_num = 0,
 	.is_div_from_arm = 1,
 	.adap_type = I2C0_ADAP_TYPE,
+        .check_idle = &i2c0_check_idle,
 };
 
 static struct resource resources_i2c0[] = {
@@ -399,10 +431,42 @@ static struct platform_device device_i2c0 = {
 #endif
 
 #ifdef CONFIG_I2C1_RK30
+static int i2c1_check_idle(void)
+{
+        int sda_level, scl_level;
+        rk30_mux_api_set(GPIO2D7_I2C1SCL_NAME, GPIO2D_GPIO2D7);	
+	rk30_mux_api_set(GPIO2D6_I2C1SDA_NAME, GPIO2D_GPIO2D6);
+
+        gpio_request(RK30_PIN2_PD7, "i2c.1");
+        gpio_request(RK30_PIN2_PD6, "i2c.1");
+        
+        gpio_direction_input(RK30_PIN2_PD7);
+        gpio_direction_input(RK30_PIN2_PD6);
+
+        sda_level = gpio_get_value(RK30_PIN2_PD7);
+        scl_level = gpio_get_value(RK30_PIN2_PD6);
+
+        gpio_free(RK30_PIN2_PD7);
+        gpio_free(RK30_PIN2_PD6);
+
+        rk30_mux_api_set(GPIO2D7_I2C1SCL_NAME, GPIO2D_I2C1_SCL);	
+	rk30_mux_api_set(GPIO2D6_I2C1SDA_NAME, GPIO2D_I2C1_SDA);
+
+        if(sda_level == 1 && scl_level == 1)
+                return I2C_IDLE;
+        else if(sda_level == 0 && scl_level == 1)
+                return I2C_SDA_LOW;
+        else if(sda_level == 1 && scl_level == 0)
+                return I2C_SCL_LOW;
+        else
+                return BOTH_LOW;
+}
+
 static struct rk30_i2c_platform_data default_i2c1_data = {
 	.bus_num = 1,
 	.is_div_from_arm = 1,
 	.adap_type = I2C1_ADAP_TYPE,
+        .check_idle = &i2c1_check_idle,
 };
 
 static struct resource resources_i2c1[] = {
@@ -430,10 +494,43 @@ static struct platform_device device_i2c1 = {
 #endif
 
 #ifdef CONFIG_I2C2_RK30
+static int i2c2_check_idle(void)
+{
+        int sda_level, scl_level;
+        rk30_mux_api_set(GPIO3A1_I2C2SCL_NAME, GPIO3A_GPIO3A1);	
+	rk30_mux_api_set(GPIO3A0_I2C2SDA_NAME, GPIO3A_GPIO3A0);
+
+        gpio_request(RK30_PIN3_PA1, "i2c.2");
+        gpio_request(RK30_PIN3_PA0, "i2c.2");
+        
+        gpio_direction_input(RK30_PIN3_PA1);
+        gpio_direction_input(RK30_PIN3_PA0);
+
+        sda_level = gpio_get_value(RK30_PIN3_PA1);
+        scl_level = gpio_get_value(RK30_PIN3_PA0);
+
+        gpio_free(RK30_PIN3_PA1);
+        gpio_free(RK30_PIN3_PA0);
+
+        rk30_mux_api_set(GPIO3A1_I2C2SCL_NAME, GPIO3A_I2C2_SCL);	
+	rk30_mux_api_set(GPIO3A0_I2C2SDA_NAME, GPIO3A_I2C2_SDA);
+
+        if(sda_level == 1 && scl_level == 1)
+                return I2C_IDLE;
+        else if(sda_level == 0 && scl_level == 1)
+                return I2C_SDA_LOW;
+        else if(sda_level == 1 && scl_level == 0)
+                return I2C_SCL_LOW;
+        else
+                return BOTH_LOW;
+}
+
+
 static struct rk30_i2c_platform_data default_i2c2_data = {
 	.bus_num = 2,
 	.is_div_from_arm = 0,
 	.adap_type = I2C2_ADAP_TYPE,
+        .check_idle = &i2c2_check_idle,
 };
 
 static struct resource resources_i2c2[] = {
@@ -461,10 +558,43 @@ static struct platform_device device_i2c2 = {
 #endif
 
 #ifdef CONFIG_I2C3_RK30
+static int i2c3_check_idle(void)
+{
+        int sda_level, scl_level;
+        rk30_mux_api_set(GPIO3A3_I2C3SCL_NAME, GPIO3A_GPIO3A3);	
+	rk30_mux_api_set(GPIO3A2_I2C3SDA_NAME, GPIO3A_GPIO3A2);
+
+        gpio_request(RK30_PIN3_PA3, "i2c.3");
+        gpio_request(RK30_PIN3_PA2, "i2c.3");
+        
+        gpio_direction_input(RK30_PIN3_PA3);
+        gpio_direction_input(RK30_PIN3_PA2);
+
+        sda_level = gpio_get_value(RK30_PIN3_PA3);
+        scl_level = gpio_get_value(RK30_PIN3_PA2);
+
+        gpio_free(RK30_PIN3_PA3);
+        gpio_free(RK30_PIN3_PA2);
+
+        rk30_mux_api_set(GPIO3A3_I2C3SCL_NAME, GPIO3A_I2C3_SCL);	
+	rk30_mux_api_set(GPIO3A2_I2C3SDA_NAME, GPIO3A_I2C3_SDA);
+
+        if(sda_level == 1 && scl_level == 1)
+                return I2C_IDLE;
+        else if(sda_level == 0 && scl_level == 1)
+                return I2C_SDA_LOW;
+        else if(sda_level == 1 && scl_level == 0)
+                return I2C_SCL_LOW;
+        else
+                return BOTH_LOW;
+}
+
+
 static struct rk30_i2c_platform_data default_i2c3_data = {
 	.bus_num = 3,
 	.is_div_from_arm = 0,
 	.adap_type = I2C3_ADAP_TYPE,
+        .check_idle = &i2c3_check_idle,
 };
 
 static struct resource resources_i2c3[] = {
@@ -492,10 +622,42 @@ static struct platform_device device_i2c3 = {
 #endif
 
 #ifdef CONFIG_I2C4_RK30
+static int i2c4_check_idle(void)
+{
+        int sda_level, scl_level;
+        rk30_mux_api_set(GPIO3A5_I2C4SCL_NAME, GPIO3A_GPIO3A5);	
+	rk30_mux_api_set(GPIO3A4_I2C4SDA_NAME, GPIO3A_GPIO3A4);
+
+        gpio_request(RK30_PIN3_PA5, "i2c.4");
+        gpio_request(RK30_PIN3_PA4, "i2c.4");
+        
+        gpio_direction_input(RK30_PIN3_PA5);
+        gpio_direction_input(RK30_PIN3_PA4);
+
+        scl_level = gpio_get_value(RK30_PIN3_PA5);
+        sda_level = gpio_get_value(RK30_PIN3_PA4);
+
+        gpio_free(RK30_PIN3_PA5);
+        gpio_free(RK30_PIN3_PA4);
+
+        rk30_mux_api_set(GPIO3A5_I2C4SCL_NAME, GPIO3A_I2C4_SCL);	
+	rk30_mux_api_set(GPIO3A4_I2C4SDA_NAME, GPIO3A_I2C4_SDA);
+
+        if(sda_level == 1 && scl_level == 1)
+                return I2C_IDLE;
+        else if(sda_level == 0 && scl_level == 1)
+                return I2C_SDA_LOW;
+        else if(sda_level == 1 && scl_level == 0)
+                return I2C_SCL_LOW;
+        else
+                return BOTH_LOW;
+}
+
 static struct rk30_i2c_platform_data default_i2c4_data = {
 	.bus_num = 4,
 	.is_div_from_arm = 0,
 	.adap_type = I2C4_ADAP_TYPE,
+        .check_idle = &i2c4_check_idle,
 };
 
 static struct resource resources_i2c4[] = {
diff --git a/arch/arm/plat-rk/include/plat/board.h b/arch/arm/plat-rk/include/plat/board.h
index 26eb6e49e573..0ab4dbc48116 100755
--- a/arch/arm/plat-rk/include/plat/board.h
+++ b/arch/arm/plat-rk/include/plat/board.h
@@ -6,6 +6,12 @@
 #include <linux/device.h>
 #include <linux/rk_screen.h>
 
+enum {
+        I2C_IDLE = 0,
+        I2C_SDA_LOW,
+        I2C_SCL_LOW,
+        BOTH_LOW,
+};
 struct rk30_i2c_platform_data {
 	char *name;
 	int bus_num;
@@ -16,6 +22,7 @@ struct rk30_i2c_platform_data {
 	u32 flags;
 	int (*io_init)(void);
 	int (*io_deinit)(void);
+        int (*check_idle)(void);
 };
 
 struct spi_cs_gpio {
diff --git a/drivers/i2c/busses/i2c-rk30-adapter.c b/drivers/i2c/busses/i2c-rk30-adapter.c
index d3374fd6f0bb..eab8a54a18c0 100755
--- a/drivers/i2c/busses/i2c-rk30-adapter.c
+++ b/drivers/i2c/busses/i2c-rk30-adapter.c
@@ -14,16 +14,19 @@
  */
 #include "i2c-rk30.h"
 
+#define COMPLETE_READ     (1<<STATE_START|1<<STATE_READ|1<<STATE_STOP)
+#define COMPLETE_WRITE     (1<<STATE_START|1<<STATE_WRITE|1<<STATE_STOP)
+
 /* Control register */
 #define I2C_CON                 0x000
 #define I2C_CON_EN              (1 << 0)
 #define I2C_CON_MOD(mod)        ((mod) << 1)
 #define I2C_CON_MASK            (3 << 1)
 enum{
-    I2C_CON_MOD_TX = 0,
-    I2C_CON_MOD_TRX,
-    I2C_CON_MOD_RX,
-    I2C_CON_MOD_RRX,
+        I2C_CON_MOD_TX = 0,
+        I2C_CON_MOD_TRX,
+        I2C_CON_MOD_RX,
+        I2C_CON_MOD_RRX,
 };
 #define I2C_CON_START           (1 << 3)
 #define I2C_CON_STOP            (1 << 4)
@@ -88,20 +91,21 @@ enum{
 static void rk30_show_regs(struct rk30_i2c *i2c)
 {
         int i;
-        i2c_dbg(i2c->dev, "i2c->start = %d\n", i2c->state);
-        i2c_dbg(i2c->dev, "I2C_CON: 0x%08x\n", i2c_readl(i2c->regs + I2C_CON));
-        i2c_dbg(i2c->dev, "I2C_CLKDIV: 0x%08x\n", i2c_readl(i2c->regs + I2C_CLKDIV));
-        i2c_dbg(i2c->dev, "I2C_MRXADDR: 0x%08x\n", i2c_readl(i2c->regs + I2C_MRXADDR));
-        i2c_dbg(i2c->dev, "I2C_MRXRADDR: 0x%08x\n", i2c_readl(i2c->regs + I2C_MRXRADDR));
-        i2c_dbg(i2c->dev, "I2C_MTXCNT: 0x%08x\n", i2c_readl(i2c->regs + I2C_MTXCNT));
-        i2c_dbg(i2c->dev, "I2C_MRXCNT: 0x%08x\n", i2c_readl(i2c->regs + I2C_MRXCNT));
-        i2c_dbg(i2c->dev, "I2C_IEN: 0x%08x\n", i2c_readl(i2c->regs + I2C_IEN));
-        i2c_dbg(i2c->dev, "I2C_IPD: 0x%08x\n", i2c_readl(i2c->regs + I2C_IPD));
-        i2c_dbg(i2c->dev, "I2C_FCNT: 0x%08x\n", i2c_readl(i2c->regs + I2C_FCNT));
+        dev_info(i2c->dev, "i2c->clk = %lu\n", clk_get_rate(i2c->clk));
+        dev_info(i2c->dev, "i2c->start = %d\n", i2c->state);
+        dev_info(i2c->dev, "I2C_CON: 0x%08x\n", i2c_readl(i2c->regs + I2C_CON));
+        dev_info(i2c->dev, "I2C_CLKDIV: 0x%08x\n", i2c_readl(i2c->regs + I2C_CLKDIV));
+        dev_info(i2c->dev, "I2C_MRXADDR: 0x%08x\n", i2c_readl(i2c->regs + I2C_MRXADDR));
+        dev_info(i2c->dev, "I2C_MRXRADDR: 0x%08x\n", i2c_readl(i2c->regs + I2C_MRXRADDR));
+        dev_info(i2c->dev, "I2C_MTXCNT: 0x%08x\n", i2c_readl(i2c->regs + I2C_MTXCNT));
+        dev_info(i2c->dev, "I2C_MRXCNT: 0x%08x\n", i2c_readl(i2c->regs + I2C_MRXCNT));
+        dev_info(i2c->dev, "I2C_IEN: 0x%08x\n", i2c_readl(i2c->regs + I2C_IEN));
+        dev_info(i2c->dev, "I2C_IPD: 0x%08x\n", i2c_readl(i2c->regs + I2C_IPD));
+        dev_info(i2c->dev, "I2C_FCNT: 0x%08x\n", i2c_readl(i2c->regs + I2C_FCNT));
         for( i = 0; i < 8; i ++) 
-                i2c_dbg(i2c->dev, "I2C_TXDATA%d: 0x%08x\n", i, i2c_readl(i2c->regs + I2C_TXDATA_BASE + i * 4));
+                dev_info(i2c->dev, "I2C_TXDATA%d: 0x%08x\n", i, i2c_readl(i2c->regs + I2C_TXDATA_BASE + i * 4));
         for( i = 0; i < 8; i ++) 
-                i2c_dbg(i2c->dev, "I2C_RXDATA%d: 0x%08x\n", i, i2c_readl(i2c->regs + I2C_RXDATA_BASE + i * 4));
+                dev_info(i2c->dev, "I2C_RXDATA%d: 0x%08x\n", i, i2c_readl(i2c->regs + I2C_RXDATA_BASE + i * 4));
 }
 static inline void rk30_i2c_enable(struct rk30_i2c *i2c, unsigned int lastnak)
 {
@@ -349,9 +353,9 @@ prepare_read:
                 }
                 rk30_i2c_clean_stop(i2c);
                 i2c_writel(I2C_STOPIPD, i2c->regs + I2C_IPD);
-                i2c->state = STATE_IDLE;
 	        i2c->is_busy = 0;
                 i2c->complete_what |= 1<<i2c->state;
+                i2c->state = STATE_IDLE;
 	        wake_up(&i2c->wait);
                 break;
         default:
@@ -455,9 +459,19 @@ static int rk30_i2c_doxfer(struct rk30_i2c *i2c,
 			      struct i2c_msg *msgs, int num)
 {
 	unsigned long timeout, flags;
-
-	if (i2c->suspended)
+        int error = 0;
+        /* 32 -- max transfer bytes
+         * 2 -- addr bytes * 2
+         * 3 -- max reg addr bytes
+         * 9 -- cycles per bytes
+         * max cycles: (32 + 2 + 3) * 9 --> 400 cycles
+         */
+        int msleep_time = 400 * 1000/ i2c->scl_rate; // ms
+
+	if (i2c->suspended){
+                dev_err(i2c->dev, "i2c is suspended\n");
 		return -EIO;
+        }
 
 	spin_lock_irqsave(&i2c->lock, flags);
 	if(rk30_i2c_set_master(i2c, msgs, num) < 0){
@@ -479,25 +493,33 @@ static int rk30_i2c_doxfer(struct rk30_i2c *i2c,
 	timeout = wait_event_timeout(i2c->wait, (i2c->is_busy == 0), msecs_to_jiffies(I2C_WAIT_TIMEOUT));
 
 	spin_lock_irqsave(&i2c->lock, flags);
-        i2c_writel(I2C_IPD_ALL_CLEAN, i2c->regs + I2C_IPD);
+        i2c->state = STATE_IDLE;
+        error = i2c->error;
 	spin_unlock_irqrestore(&i2c->lock, flags);
 
 	if (timeout == 0){
-                dev_err(i2c->dev, "addr[0x%02x] wait event timeout, state: %d, is_busy: %d, error: %d, complete_what: 0x%x\n", 
-                                msgs[0].addr, i2c->state, i2c->is_busy, i2c->error, i2c->complete_what);  
-                i2c->state = STATE_IDLE;
-                if((i2c->error < 0) || (i2c->is_busy != 0) || !(i2c->complete_what & (1 << STATE_STOP))) 
-                        i2c->error = -ETIMEDOUT;
-                rk30_i2c_send_stop(i2c);
-
+                if(error < 0)
+                        i2c_dbg(i2c->dev, "error = %d\n", error);
+                else if((i2c->complete_what !=COMPLETE_READ  && i2c->complete_what != COMPLETE_WRITE)){
+                        dev_err(i2c->dev, "Addr[0x%02x] wait event timeout, state: %d, is_busy: %d, error: %d, complete_what: 0x%x, ipd: 0x%x\n", 
+                                msgs[0].addr, i2c->state, i2c->is_busy, error, i2c->complete_what, i2c_readl(i2c->regs + I2C_IPD));  
+                        //rk30_show_regs(i2c);
+                        error = -ETIMEDOUT;
+                        msleep(msleep_time);
+                        rk30_i2c_send_stop(i2c);
+                        msleep(1);
+                }
+                else
+                        i2c_dbg(i2c->dev, "Addr[0x%02x] wait event timeout, but transfer complete\n", i2c->addr);  
         }
+        i2c_writel(I2C_IPD_ALL_CLEAN, i2c->regs + I2C_IPD);
 	rk30_i2c_disable_irq(i2c);
         rk30_i2c_disable(i2c);
 
-        if(i2c->error == -EAGAIN)
-                dev_err(i2c->dev, "No ack(retry: %d, complete_what: 0x%x), Maybe slave(addr: 0x%02x) not exist or abnormal power-on\n",
-                                i2c->adap.retries + 1, i2c->complete_what, i2c->addr);
-	return i2c->error;
+        if(error == -EAGAIN)
+                i2c_dbg(i2c->dev, "No ack(complete_what: 0x%x), Maybe slave(addr: 0x%02x) not exist or abnormal power-on\n",
+                                i2c->complete_what, i2c->addr);
+	return error;
 }
 
 /* rk30_i2c_xfer
@@ -509,11 +531,18 @@ static int rk30_i2c_doxfer(struct rk30_i2c *i2c,
 static int rk30_i2c_xfer(struct i2c_adapter *adap,
 			struct i2c_msg *msgs, int num)
 {
-	int ret = 0;
+	int ret = 0, state, retry = 10;
         unsigned long scl_rate;
 	struct rk30_i2c *i2c = (struct rk30_i2c *)adap->algo_data;
 
         clk_enable(i2c->clk);
+        while(retry-- && ((state = i2c->check_idle()) != I2C_IDLE)){
+                msleep(10);
+        }
+        if(retry == 0){
+                dev_err(i2c->dev, "i2c is not in idle(state = %d)\n", state);
+                return -EIO;
+        }
 
         if(msgs[0].scl_rate <= 400000 && msgs[0].scl_rate >= 10000)
 		scl_rate = msgs[0].scl_rate;
diff --git a/drivers/i2c/busses/i2c-rk30.c b/drivers/i2c/busses/i2c-rk30.c
index ccd3777bb91f..47831d3e57d9 100755
--- a/drivers/i2c/busses/i2c-rk30.c
+++ b/drivers/i2c/busses/i2c-rk30.c
@@ -100,6 +100,9 @@ static int rk30_i2c_probe(struct platform_device *pdev)
 
         if(pdata->io_init)
 		pdata->io_init();
+        if(pdata->check_idle){
+                i2c->check_idle = pdata->check_idle;
+        }
 
 	strlcpy(i2c->adap.name, "rk30_i2c", sizeof(i2c->adap.name));
 	i2c->adap.owner   = THIS_MODULE;
@@ -124,7 +127,7 @@ static int rk30_i2c_probe(struct platform_device *pdev)
 
 	i2c_dbg(&pdev->dev, "clock source %p\n", i2c->clk);
 
-	//clk_enable(i2c->clk);
+	clk_enable(i2c->clk);
 
 	/* map the registers */
 
diff --git a/drivers/i2c/busses/i2c-rk30.h b/drivers/i2c/busses/i2c-rk30.h
index afb2d33721bc..ccdc7f9c0293 100755
--- a/drivers/i2c/busses/i2c-rk30.h
+++ b/drivers/i2c/busses/i2c-rk30.h
@@ -68,7 +68,7 @@ struct rk30_i2c {
         };
         union {
 	        unsigned int		msg_idx;
-	        unsigned int		error;
+	        int		        error;
         };
 	unsigned int		msg_ptr;
 
@@ -101,6 +101,7 @@ struct rk30_i2c {
 
         void (*i2c_init_hw)(struct rk30_i2c *, unsigned long scl_rate);
         void (*i2c_set_clk)(struct rk30_i2c *, unsigned long);
+        int (*check_idle)(void);
         irqreturn_t (*i2c_irq)(int, void *);
 };
 void i2c_adap_sel(struct rk30_i2c *i2c, int nr, int adap_type);
diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c
index d37266328952..2da28eb212b8 100755
--- a/drivers/i2c/i2c-core.c
+++ b/drivers/i2c/i2c-core.c
@@ -1411,6 +1411,8 @@ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
 			ret = adap->algo->master_xfer(adap, msgs, num);
 			if (ret != -EAGAIN)
 				break;
+		        dev_err(&adap->dev, "No ack, Maybe slave(addr: 0x%x) not exist or abnormal power-on, retry %d...\n", 
+                                        msgs[0].addr, adap->retries - try);
 			if (time_after(jiffies, orig_jiffies + adap->timeout))
 				break;
 		}
-- 
2.34.1