Skip to content

Commit

Permalink
i2c: sirf: reset i2c controller early after we get a noack
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Zhiwu Song authored and Wolfram Sang committed Aug 28, 2013
1 parent 57cd1e3 commit c984319
Showing 1 changed file with 18 additions and 9 deletions.
27 changes: 18 additions & 9 deletions drivers/i2c/busses/i2c-sirf.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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);

Expand All @@ -199,24 +210,22 @@ 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");
}

writel(regval & ~(SIRFSOC_I2C_CMD_DONE_EN | SIRFSOC_I2C_ERR_INT_EN),
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)
Expand Down

0 comments on commit c984319

Please sign in to comment.