diff options
author | Rajendra Nayak <rnayak@ti.com> | 2009-11-23 11:59:18 -0500 |
---|---|---|
committer | Ben Dooks <ben-linux@fluff.org> | 2009-12-08 19:19:13 -0500 |
commit | ef871432e1334dea4c79f9875f4db87cee7b9b50 (patch) | |
tree | 54f3a4ae1853429f5cd23ffe05bca3913f0980ce /drivers/i2c | |
parent | 6d1ea0f6afde6887d6dea2ace1714a23d9b5820d (diff) |
i2c-omap: OMAP3: PM: (re)init for every transfer to support off-mode
Because of OMAP off-mode, powerdomain can go off when I2C is idle.
Save enough state, and do a re-init for each transfer.
Additional save/restore state added by Jagadeesh Bhaskar Pakaravoor
(SYSC_REG) and Aaro Koskinen (wakeup sources.)
Also, The OMAP3430 TRM states:
"During active mode (I2Ci.I2C_CON[15] I2C_EN bit is set to 1), make no
changes to the I2Ci.I2C_SCLL and I2Ci.I2C_SCLH registers. Changes may
result in unpredictable behavior."
Hence, the I2C_EN bit should be clearer when modifying these
registers. Please note that clearing the entire I2C_CON register to
disable the I2C module is safe, because the I2C_CON register is
re-configured for each transfer.
Signed-off-by: Jouni Hogander <jouni.hogander@nokia.com>
Signed-off-by: Rajendra Nayak <rnayak@ti.com>
Cc: Jagadeesh Bhaskar Pakaravoor <j-pakaravoor@ti.com>
Cc: Aaro Koskinen <aaro.koskinen@nokia.com>
Cc: Jon Hunter <jon-hunter@ti.com>
Cc: Hu Tao <taohu@motorola.com>
Cc: Xiaolong Chen <A21785@motorola.com>
Signed-off-by: Kevin Hilman <khilman@deeprootsystems.com>
Signed-off-by: Ben Dooks <ben-linux@fluff.org>
Diffstat (limited to 'drivers/i2c')
-rw-r--r-- | drivers/i2c/busses/i2c-omap.c | 64 |
1 files changed, 41 insertions, 23 deletions
diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c index 827da0858136..75bf3ad18099 100644 --- a/drivers/i2c/busses/i2c-omap.c +++ b/drivers/i2c/busses/i2c-omap.c | |||
@@ -178,6 +178,12 @@ struct omap_i2c_dev { | |||
178 | unsigned b_hw:1; /* bad h/w fixes */ | 178 | unsigned b_hw:1; /* bad h/w fixes */ |
179 | unsigned idle:1; | 179 | unsigned idle:1; |
180 | u16 iestate; /* Saved interrupt register */ | 180 | u16 iestate; /* Saved interrupt register */ |
181 | u16 pscstate; | ||
182 | u16 scllstate; | ||
183 | u16 sclhstate; | ||
184 | u16 bufstate; | ||
185 | u16 syscstate; | ||
186 | u16 westate; | ||
181 | }; | 187 | }; |
182 | 188 | ||
183 | static inline void omap_i2c_write_reg(struct omap_i2c_dev *i2c_dev, | 189 | static inline void omap_i2c_write_reg(struct omap_i2c_dev *i2c_dev, |
@@ -230,9 +236,18 @@ static void omap_i2c_unidle(struct omap_i2c_dev *dev) | |||
230 | 236 | ||
231 | clk_enable(dev->iclk); | 237 | clk_enable(dev->iclk); |
232 | clk_enable(dev->fclk); | 238 | clk_enable(dev->fclk); |
239 | if (cpu_is_omap34xx()) { | ||
240 | omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, 0); | ||
241 | omap_i2c_write_reg(dev, OMAP_I2C_PSC_REG, dev->pscstate); | ||
242 | omap_i2c_write_reg(dev, OMAP_I2C_SCLL_REG, dev->scllstate); | ||
243 | omap_i2c_write_reg(dev, OMAP_I2C_SCLH_REG, dev->sclhstate); | ||
244 | omap_i2c_write_reg(dev, OMAP_I2C_BUF_REG, dev->bufstate); | ||
245 | omap_i2c_write_reg(dev, OMAP_I2C_SYSC_REG, dev->syscstate); | ||
246 | omap_i2c_write_reg(dev, OMAP_I2C_WE_REG, dev->westate); | ||
247 | omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, OMAP_I2C_CON_EN); | ||
248 | } | ||
233 | dev->idle = 0; | 249 | dev->idle = 0; |
234 | if (dev->iestate) | 250 | omap_i2c_write_reg(dev, OMAP_I2C_IE_REG, dev->iestate); |
235 | omap_i2c_write_reg(dev, OMAP_I2C_IE_REG, dev->iestate); | ||
236 | } | 251 | } |
237 | 252 | ||
238 | static void omap_i2c_idle(struct omap_i2c_dev *dev) | 253 | static void omap_i2c_idle(struct omap_i2c_dev *dev) |
@@ -258,7 +273,7 @@ static void omap_i2c_idle(struct omap_i2c_dev *dev) | |||
258 | 273 | ||
259 | static int omap_i2c_init(struct omap_i2c_dev *dev) | 274 | static int omap_i2c_init(struct omap_i2c_dev *dev) |
260 | { | 275 | { |
261 | u16 psc = 0, scll = 0, sclh = 0; | 276 | u16 psc = 0, scll = 0, sclh = 0, buf = 0; |
262 | u16 fsscll = 0, fssclh = 0, hsscll = 0, hssclh = 0; | 277 | u16 fsscll = 0, fssclh = 0, hsscll = 0, hssclh = 0; |
263 | unsigned long fclk_rate = 12000000; | 278 | unsigned long fclk_rate = 12000000; |
264 | unsigned long timeout; | 279 | unsigned long timeout; |
@@ -287,24 +302,22 @@ static int omap_i2c_init(struct omap_i2c_dev *dev) | |||
287 | SYSC_AUTOIDLE_MASK); | 302 | SYSC_AUTOIDLE_MASK); |
288 | 303 | ||
289 | } else if (dev->rev >= OMAP_I2C_REV_ON_3430) { | 304 | } else if (dev->rev >= OMAP_I2C_REV_ON_3430) { |
290 | u32 v; | 305 | dev->syscstate = SYSC_AUTOIDLE_MASK; |
291 | 306 | dev->syscstate |= SYSC_ENAWAKEUP_MASK; | |
292 | v = SYSC_AUTOIDLE_MASK; | 307 | dev->syscstate |= (SYSC_IDLEMODE_SMART << |
293 | v |= SYSC_ENAWAKEUP_MASK; | ||
294 | v |= (SYSC_IDLEMODE_SMART << | ||
295 | __ffs(SYSC_SIDLEMODE_MASK)); | 308 | __ffs(SYSC_SIDLEMODE_MASK)); |
296 | v |= (SYSC_CLOCKACTIVITY_FCLK << | 309 | dev->syscstate |= (SYSC_CLOCKACTIVITY_FCLK << |
297 | __ffs(SYSC_CLOCKACTIVITY_MASK)); | 310 | __ffs(SYSC_CLOCKACTIVITY_MASK)); |
298 | 311 | ||
299 | omap_i2c_write_reg(dev, OMAP_I2C_SYSC_REG, v); | 312 | omap_i2c_write_reg(dev, OMAP_I2C_SYSC_REG, |
313 | dev->syscstate); | ||
300 | /* | 314 | /* |
301 | * Enabling all wakup sources to stop I2C freezing on | 315 | * Enabling all wakup sources to stop I2C freezing on |
302 | * WFI instruction. | 316 | * WFI instruction. |
303 | * REVISIT: Some wkup sources might not be needed. | 317 | * REVISIT: Some wkup sources might not be needed. |
304 | */ | 318 | */ |
305 | omap_i2c_write_reg(dev, OMAP_I2C_WE_REG, | 319 | dev->westate = OMAP_I2C_WE_ALL; |
306 | OMAP_I2C_WE_ALL); | 320 | omap_i2c_write_reg(dev, OMAP_I2C_WE_REG, dev->westate); |
307 | |||
308 | } | 321 | } |
309 | } | 322 | } |
310 | omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, 0); | 323 | omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, 0); |
@@ -394,23 +407,28 @@ static int omap_i2c_init(struct omap_i2c_dev *dev) | |||
394 | omap_i2c_write_reg(dev, OMAP_I2C_SCLL_REG, scll); | 407 | omap_i2c_write_reg(dev, OMAP_I2C_SCLL_REG, scll); |
395 | omap_i2c_write_reg(dev, OMAP_I2C_SCLH_REG, sclh); | 408 | omap_i2c_write_reg(dev, OMAP_I2C_SCLH_REG, sclh); |
396 | 409 | ||
397 | if (dev->fifo_size) | 410 | if (dev->fifo_size) { |
398 | /* Note: setup required fifo size - 1 */ | 411 | /* Note: setup required fifo size - 1. RTRSH and XTRSH */ |
399 | omap_i2c_write_reg(dev, OMAP_I2C_BUF_REG, | 412 | buf = (dev->fifo_size - 1) << 8 | OMAP_I2C_BUF_RXFIF_CLR | |
400 | (dev->fifo_size - 1) << 8 | /* RTRSH */ | 413 | (dev->fifo_size - 1) | OMAP_I2C_BUF_TXFIF_CLR; |
401 | OMAP_I2C_BUF_RXFIF_CLR | | 414 | omap_i2c_write_reg(dev, OMAP_I2C_BUF_REG, buf); |
402 | (dev->fifo_size - 1) | /* XTRSH */ | 415 | } |
403 | OMAP_I2C_BUF_TXFIF_CLR); | ||
404 | 416 | ||
405 | /* Take the I2C module out of reset: */ | 417 | /* Take the I2C module out of reset: */ |
406 | omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, OMAP_I2C_CON_EN); | 418 | omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, OMAP_I2C_CON_EN); |
407 | 419 | ||
408 | /* Enable interrupts */ | 420 | /* Enable interrupts */ |
409 | omap_i2c_write_reg(dev, OMAP_I2C_IE_REG, | 421 | dev->iestate = (OMAP_I2C_IE_XRDY | OMAP_I2C_IE_RRDY | |
410 | (OMAP_I2C_IE_XRDY | OMAP_I2C_IE_RRDY | | ||
411 | OMAP_I2C_IE_ARDY | OMAP_I2C_IE_NACK | | 422 | OMAP_I2C_IE_ARDY | OMAP_I2C_IE_NACK | |
412 | OMAP_I2C_IE_AL) | ((dev->fifo_size) ? | 423 | OMAP_I2C_IE_AL) | ((dev->fifo_size) ? |
413 | (OMAP_I2C_IE_RDR | OMAP_I2C_IE_XDR) : 0)); | 424 | (OMAP_I2C_IE_RDR | OMAP_I2C_IE_XDR) : 0); |
425 | omap_i2c_write_reg(dev, OMAP_I2C_IE_REG, dev->iestate); | ||
426 | if (cpu_is_omap34xx()) { | ||
427 | dev->pscstate = psc; | ||
428 | dev->scllstate = scll; | ||
429 | dev->sclhstate = sclh; | ||
430 | dev->bufstate = buf; | ||
431 | } | ||
414 | return 0; | 432 | return 0; |
415 | } | 433 | } |
416 | 434 | ||