From 7aa57a01c0b5589c6f757b7a5106e09f9ba8fb10 Mon Sep 17 00:00:00 2001 From: Troy Kisky Date: Thu, 19 Jul 2012 08:18:09 +0000 Subject: mxc_i2c: combine i2c_imx_bus_busy and i2c_imx_trx_complete into wait_for_sr_state Not using udelay gives a more accurate timeout. The current implementation of udelay in imx-common does not seem to wait at all for a udelay(1). Signed-off-by: Troy Kisky Acked-by: Marek Vasut ---- V2: Added WATCHDOG_RESET as suggested by Marek Vasut add error message when stop fails mxc_i2c: code i2c_probe as a 0 length i2c_write Signed-off-by: Troy Kisky Acked-by: Marek Vasut Acked-by: Stefano Babic --- drivers/i2c/mxc_i2c.c | 80 +++++++++++++++++++-------------------------------- 1 file changed, 29 insertions(+), 51 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/mxc_i2c.c b/drivers/i2c/mxc_i2c.c index 626e13eb11..01a64081d7 100644 --- a/drivers/i2c/mxc_i2c.c +++ b/drivers/i2c/mxc_i2c.c @@ -36,6 +36,7 @@ #include #include #include +#include struct mxc_i2c_regs { uint32_t iadr; @@ -63,8 +64,6 @@ struct mxc_i2c_regs { #error "define CONFIG_SYS_I2C_BASE to use the mxc_i2c driver" #endif -#define I2C_MAX_TIMEOUT 10000 - static u16 i2c_clk_div[50][2] = { { 22, 0x20 }, { 24, 0x21 }, { 26, 0x22 }, { 28, 0x23 }, { 30, 0x00 }, { 32, 0x24 }, { 36, 0x25 }, { 40, 0x26 }, @@ -164,48 +163,26 @@ unsigned int i2c_get_bus_speed(void) return mxc_get_clock(MXC_IPG_PERCLK) / i2c_clk_div[clk_div][0]; } -/* - * Wait for bus to be busy (or free if for_busy = 0) - * - * for_busy = 1: Wait for IBB to be asserted - * for_busy = 0: Wait for IBB to be de-asserted - */ -int i2c_imx_bus_busy(int for_busy) -{ - struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE; - unsigned int temp; - - int timeout = I2C_MAX_TIMEOUT; - - while (timeout--) { - temp = readb(&i2c_regs->i2sr); - - if (for_busy && (temp & I2SR_IBB)) - return 0; - if (!for_busy && !(temp & I2SR_IBB)) - return 0; - - udelay(1); - } - - return 1; -} +#define ST_BUS_IDLE (0 | (I2SR_IBB << 8)) +#define ST_BUS_BUSY (I2SR_IBB | (I2SR_IBB << 8)) +#define ST_IIF (I2SR_IIF | (I2SR_IIF << 8)) -/* - * Wait for transaction to complete - */ -int i2c_imx_trx_complete(void) +static int wait_for_sr_state(struct mxc_i2c_regs *i2c_regs, unsigned state) { - struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE; - int timeout = I2C_MAX_TIMEOUT; - - while (timeout--) { - if (readb(&i2c_regs->i2sr) & I2SR_IIF) - return 0; - - udelay(1); + unsigned sr; + ulong elapsed; + ulong start_time = get_timer(0); + for (;;) { + sr = readb(&i2c_regs->i2sr); + if ((sr & (state >> 8)) == (unsigned char)state) + return sr; + WATCHDOG_RESET(); + elapsed = get_timer(start_time); + if (elapsed > (CONFIG_SYS_HZ / 10)) /* .1 seconds */ + break; } - + printf("%s: failed sr=%x cr=%x state=%x\n", __func__, + sr, readb(&i2c_regs->i2cr), state); return -ETIMEDOUT; } @@ -215,7 +192,7 @@ static int tx_byte(struct mxc_i2c_regs *i2c_regs, u8 byte) writeb(0, &i2c_regs->i2sr); writeb(byte, &i2c_regs->i2dr); - ret = i2c_imx_trx_complete(); + ret = wait_for_sr_state(i2c_regs, ST_IIF); if (ret < 0) return ret; ret = readb(&i2c_regs->i2sr); @@ -245,8 +222,8 @@ int i2c_imx_start(void) temp |= I2CR_MSTA; writeb(temp, &i2c_regs->i2cr); - result = i2c_imx_bus_busy(1); - if (result) + result = wait_for_sr_state(i2c_regs, ST_BUS_BUSY); + if (result < 0) return result; temp |= I2CR_MTX | I2CR_TX_NO_AK; @@ -260,6 +237,7 @@ int i2c_imx_start(void) */ void i2c_imx_stop(void) { + int ret; struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE; unsigned int temp = 0; @@ -268,8 +246,9 @@ void i2c_imx_stop(void) temp &= ~(I2CR_MSTA | I2CR_MTX); writeb(temp, &i2c_regs->i2cr); - i2c_imx_bus_busy(0); - + ret = wait_for_sr_state(i2c_regs, ST_BUS_IDLE); + if (ret < 0) + printf("%s:trigger stop failed\n", __func__); /* Disable I2C controller */ writeb(0, &i2c_regs->i2cr); } @@ -336,8 +315,8 @@ int i2c_read(uchar chip, uint addr, int alen, uchar *buf, int len) /* read data */ for (i = 0; i < len; i++) { - ret = i2c_imx_trx_complete(); - if (ret) { + ret = wait_for_sr_state(i2c_regs, ST_IIF); + if (ret < 0) { i2c_imx_stop(); return ret; } @@ -350,20 +329,19 @@ int i2c_read(uchar chip, uint addr, int alen, uchar *buf, int len) temp = readb(&i2c_regs->i2cr); temp &= ~(I2CR_MSTA | I2CR_MTX); writeb(temp, &i2c_regs->i2cr); - i2c_imx_bus_busy(0); + wait_for_sr_state(i2c_regs, ST_BUS_IDLE); } else if (i == (len - 2)) { temp = readb(&i2c_regs->i2cr); temp |= I2CR_TX_NO_AK; writeb(temp, &i2c_regs->i2cr); } - writeb(0, &i2c_regs->i2sr); buf[i] = readb(&i2c_regs->i2dr); } i2c_imx_stop(); - return ret; + return 0; } /* -- cgit