mirror of
https://github.com/torvalds/linux.git
synced 2024-11-05 11:32:04 +00:00
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:
parent
57cd1e3029
commit
c984319ad9
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user