]> Pileus Git - ~andy/linux/commitdiff
i2c: sirf: reset i2c controller early after we get a noack
authorZhiwu Song <Zhiwu.Song@csr.com>
Tue, 13 Aug 2013 09:11:27 +0000 (17:11 +0800)
committerWolfram Sang <wsa@the-dreams.de>
Wed, 28 Aug 2013 09:43:34 +0000 (11:43 +0200)
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>
drivers/i2c/busses/i2c-sirf.c

index 0ff22e29e7df5477fcbd2b5a094d4edbc03c85b5..ff47a767f7569aec2402fba299852624cd7b6223 100644 (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)