From 37aaf072da3697f832011c1f4d238b1356386d67 Mon Sep 17 00:00:00 2001 From: kfx 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 #include +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<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<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