diff options
author | Zhiwu Song <Zhiwu.Song@csr.com> | 2013-08-13 05:11:27 -0400 |
---|---|---|
committer | Wolfram Sang <wsa@the-dreams.de> | 2013-08-28 05:43:34 -0400 |
commit | c984319ad9c54c269493b6bc0517e8c2dfca4456 (patch) | |
tree | 87a41ed1f90e639973c321caec782445eb0c1b96 /drivers/i2c | |
parent | 57cd1e3029e5fb4d238ad11fd0d7ad917179fdf2 (diff) |
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>
Diffstat (limited to 'drivers/i2c')
-rw-r--r-- | drivers/i2c/busses/i2c-sirf.c | 27 |
1 files changed, 18 insertions, 9 deletions
diff --git a/drivers/i2c/busses/i2c-sirf.c b/drivers/i2c/busses/i2c-sirf.c index 0ff22e29e7df..ff47a767f756 100644 --- a/drivers/i2c/busses/i2c-sirf.c +++ b/drivers/i2c/busses/i2c-sirf.c | |||
@@ -64,6 +64,8 @@ | |||
64 | #define SIRFSOC_I2C_START BIT(7) | 64 | #define SIRFSOC_I2C_START BIT(7) |
65 | 65 | ||
66 | #define SIRFSOC_I2C_DEFAULT_SPEED 100000 | 66 | #define SIRFSOC_I2C_DEFAULT_SPEED 100000 |
67 | #define SIRFSOC_I2C_ERR_NOACK 1 | ||
68 | #define SIRFSOC_I2C_ERR_TIMEOUT 2 | ||
67 | 69 | ||
68 | struct sirfsoc_i2c { | 70 | struct sirfsoc_i2c { |
69 | void __iomem *base; | 71 | void __iomem *base; |
@@ -142,14 +144,24 @@ static irqreturn_t i2c_sirfsoc_irq(int irq, void *dev_id) | |||
142 | 144 | ||
143 | if (i2c_stat & SIRFSOC_I2C_STAT_ERR) { | 145 | if (i2c_stat & SIRFSOC_I2C_STAT_ERR) { |
144 | /* Error conditions */ | 146 | /* Error conditions */ |
145 | siic->err_status = 1; | 147 | siic->err_status = SIRFSOC_I2C_ERR_NOACK; |
146 | writel(SIRFSOC_I2C_STAT_ERR, siic->base + SIRFSOC_I2C_STATUS); | 148 | writel(SIRFSOC_I2C_STAT_ERR, siic->base + SIRFSOC_I2C_STATUS); |
147 | 149 | ||
148 | if (i2c_stat & SIRFSOC_I2C_STAT_NACK) | 150 | if (i2c_stat & SIRFSOC_I2C_STAT_NACK) |
149 | dev_err(&siic->adapter.dev, "ACK not received\n"); | 151 | dev_dbg(&siic->adapter.dev, "ACK not received\n"); |
150 | else | 152 | else |
151 | dev_err(&siic->adapter.dev, "I2C error\n"); | 153 | dev_err(&siic->adapter.dev, "I2C error\n"); |
152 | 154 | ||
155 | /* | ||
156 | * Due to hardware ANOMALY, we need to reset I2C earlier after | ||
157 | * we get NOACK while accessing non-existing clients, otherwise | ||
158 | * we will get errors even we access existing clients later | ||
159 | */ | ||
160 | writel(readl(siic->base + SIRFSOC_I2C_CTRL) | SIRFSOC_I2C_RESET, | ||
161 | siic->base + SIRFSOC_I2C_CTRL); | ||
162 | while (readl(siic->base + SIRFSOC_I2C_CTRL) & SIRFSOC_I2C_RESET) | ||
163 | cpu_relax(); | ||
164 | |||
153 | complete(&siic->done); | 165 | complete(&siic->done); |
154 | } else if (i2c_stat & SIRFSOC_I2C_STAT_CMD_DONE) { | 166 | } else if (i2c_stat & SIRFSOC_I2C_STAT_CMD_DONE) { |
155 | /* CMD buffer execution complete */ | 167 | /* CMD buffer execution complete */ |
@@ -190,7 +202,6 @@ static int i2c_sirfsoc_xfer_msg(struct sirfsoc_i2c *siic, struct i2c_msg *msg) | |||
190 | u32 regval = readl(siic->base + SIRFSOC_I2C_CTRL); | 202 | u32 regval = readl(siic->base + SIRFSOC_I2C_CTRL); |
191 | /* timeout waiting for the xfer to finish or fail */ | 203 | /* timeout waiting for the xfer to finish or fail */ |
192 | int timeout = msecs_to_jiffies((msg->len + 1) * 50); | 204 | int timeout = msecs_to_jiffies((msg->len + 1) * 50); |
193 | int ret = 0; | ||
194 | 205 | ||
195 | i2c_sirfsoc_set_address(siic, msg); | 206 | i2c_sirfsoc_set_address(siic, msg); |
196 | 207 | ||
@@ -199,7 +210,7 @@ static int i2c_sirfsoc_xfer_msg(struct sirfsoc_i2c *siic, struct i2c_msg *msg) | |||
199 | i2c_sirfsoc_queue_cmd(siic); | 210 | i2c_sirfsoc_queue_cmd(siic); |
200 | 211 | ||
201 | if (wait_for_completion_timeout(&siic->done, timeout) == 0) { | 212 | if (wait_for_completion_timeout(&siic->done, timeout) == 0) { |
202 | siic->err_status = 1; | 213 | siic->err_status = SIRFSOC_I2C_ERR_TIMEOUT; |
203 | dev_err(&siic->adapter.dev, "Transfer timeout\n"); | 214 | dev_err(&siic->adapter.dev, "Transfer timeout\n"); |
204 | } | 215 | } |
205 | 216 | ||
@@ -207,16 +218,14 @@ static int i2c_sirfsoc_xfer_msg(struct sirfsoc_i2c *siic, struct i2c_msg *msg) | |||
207 | siic->base + SIRFSOC_I2C_CTRL); | 218 | siic->base + SIRFSOC_I2C_CTRL); |
208 | writel(0, siic->base + SIRFSOC_I2C_CMD_START); | 219 | writel(0, siic->base + SIRFSOC_I2C_CMD_START); |
209 | 220 | ||
210 | if (siic->err_status) { | 221 | /* i2c control doesn't response, reset it */ |
222 | if (siic->err_status == SIRFSOC_I2C_ERR_TIMEOUT) { | ||
211 | writel(readl(siic->base + SIRFSOC_I2C_CTRL) | SIRFSOC_I2C_RESET, | 223 | writel(readl(siic->base + SIRFSOC_I2C_CTRL) | SIRFSOC_I2C_RESET, |
212 | siic->base + SIRFSOC_I2C_CTRL); | 224 | siic->base + SIRFSOC_I2C_CTRL); |
213 | while (readl(siic->base + SIRFSOC_I2C_CTRL) & SIRFSOC_I2C_RESET) | 225 | while (readl(siic->base + SIRFSOC_I2C_CTRL) & SIRFSOC_I2C_RESET) |
214 | cpu_relax(); | 226 | cpu_relax(); |
215 | |||
216 | ret = -EIO; | ||
217 | } | 227 | } |
218 | 228 | return siic->err_status ? -EIO : 0; | |
219 | return ret; | ||
220 | } | 229 | } |
221 | 230 | ||
222 | static u32 i2c_sirfsoc_func(struct i2c_adapter *adap) | 231 | static u32 i2c_sirfsoc_func(struct i2c_adapter *adap) |