i2c: sirf: reset i2c controller early after we get a noack

Due to hardware ANOMALY, we need to reset I2C earlier after
we get NOACK while accessing non-existing clients, otherwise
we will get errors even we access existing clients later

Signed-off-by: Zhiwu Song <Zhiwu.Song@csr.com>
Signed-off-by: Barry Song <Baohua.Song@csr.com>
Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
This commit is contained in:
Zhiwu Song 2013-08-13 17:11:27 +08:00 committed by Wolfram Sang
parent 57cd1e3029
commit c984319ad9

View File

@ -64,6 +64,8 @@
#define SIRFSOC_I2C_START BIT(7)
#define SIRFSOC_I2C_DEFAULT_SPEED 100000
#define SIRFSOC_I2C_ERR_NOACK 1
#define SIRFSOC_I2C_ERR_TIMEOUT 2
struct sirfsoc_i2c {
void __iomem *base;
@ -142,14 +144,24 @@ static irqreturn_t i2c_sirfsoc_irq(int irq, void *dev_id)
if (i2c_stat & SIRFSOC_I2C_STAT_ERR) {
/* Error conditions */
siic->err_status = 1;
siic->err_status = SIRFSOC_I2C_ERR_NOACK;
writel(SIRFSOC_I2C_STAT_ERR, siic->base + SIRFSOC_I2C_STATUS);
if (i2c_stat & SIRFSOC_I2C_STAT_NACK)
dev_err(&siic->adapter.dev, "ACK not received\n");
dev_dbg(&siic->adapter.dev, "ACK not received\n");
else
dev_err(&siic->adapter.dev, "I2C error\n");
/*
* Due to hardware ANOMALY, we need to reset I2C earlier after
* we get NOACK while accessing non-existing clients, otherwise
* we will get errors even we access existing clients later
*/
writel(readl(siic->base + SIRFSOC_I2C_CTRL) | SIRFSOC_I2C_RESET,
siic->base + SIRFSOC_I2C_CTRL);
while (readl(siic->base + SIRFSOC_I2C_CTRL) & SIRFSOC_I2C_RESET)
cpu_relax();
complete(&siic->done);
} else if (i2c_stat & SIRFSOC_I2C_STAT_CMD_DONE) {
/* CMD buffer execution complete */
@ -190,7 +202,6 @@ static int i2c_sirfsoc_xfer_msg(struct sirfsoc_i2c *siic, struct i2c_msg *msg)
u32 regval = readl(siic->base + SIRFSOC_I2C_CTRL);
/* timeout waiting for the xfer to finish or fail */
int timeout = msecs_to_jiffies((msg->len + 1) * 50);
int ret = 0;
i2c_sirfsoc_set_address(siic, msg);
@ -199,7 +210,7 @@ static int i2c_sirfsoc_xfer_msg(struct sirfsoc_i2c *siic, struct i2c_msg *msg)
i2c_sirfsoc_queue_cmd(siic);
if (wait_for_completion_timeout(&siic->done, timeout) == 0) {
siic->err_status = 1;
siic->err_status = SIRFSOC_I2C_ERR_TIMEOUT;
dev_err(&siic->adapter.dev, "Transfer timeout\n");
}
@ -207,16 +218,14 @@ static int i2c_sirfsoc_xfer_msg(struct sirfsoc_i2c *siic, struct i2c_msg *msg)
siic->base + SIRFSOC_I2C_CTRL);
writel(0, siic->base + SIRFSOC_I2C_CMD_START);
if (siic->err_status) {
/* i2c control doesn't response, reset it */
if (siic->err_status == SIRFSOC_I2C_ERR_TIMEOUT) {
writel(readl(siic->base + SIRFSOC_I2C_CTRL) | SIRFSOC_I2C_RESET,
siic->base + SIRFSOC_I2C_CTRL);
while (readl(siic->base + SIRFSOC_I2C_CTRL) & SIRFSOC_I2C_RESET)
cpu_relax();
ret = -EIO;
}
return ret;
return siic->err_status ? -EIO : 0;
}
static u32 i2c_sirfsoc_func(struct i2c_adapter *adap)