aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/i2c
diff options
context:
space:
mode:
authorTony Lindgren <tony@atomide.com>2008-03-23 15:28:20 -0400
committerJean Delvare <khali@hyperion.delvare>2008-03-23 15:28:20 -0400
commitf08ac4e79424c266aed8282939649104b37f53b4 (patch)
treecf41c7a0e3f388a91c39d77a0ce43bad5333b902 /drivers/i2c
parent70849251147c3499afb8d7500d0fba240da73456 (diff)
i2c-omap: Fix unhandled fault
If an I2C interrupt happens between disabling interface clock and functional clock, the interrupt handler will produce an external abort on non-linefetch error when trying to access driver registers while interface clock is disabled. This patch fixes the problem by saving and disabling i2c-omap interrupt before turning off the clocks. Also disable functional clock before the interface clock as suggested by Paul Walmsley. Patch also renames enable/disable_clocks functions to unidle/idle functions. Note that the driver is currently not taking advantage of the idle interrupts. To use the idle interrupts, driver would have to enable interface clock based on the idle interrupt and dev->idle flag. This patch has been tested in linux-omap tree with various omaps. Cc: Paul Walmsley <paul@pwsan.com> Signed-off-by: Tony Lindgren <tony@atomide.com> Signed-off-by: Jean Delvare <khali@linux-fr.org>
Diffstat (limited to 'drivers/i2c')
-rw-r--r--drivers/i2c/busses/i2c-omap.c36
1 files changed, 28 insertions, 8 deletions
diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c
index da6639707ea3..7ba31770d773 100644
--- a/drivers/i2c/busses/i2c-omap.c
+++ b/drivers/i2c/busses/i2c-omap.c
@@ -128,6 +128,8 @@ struct omap_i2c_dev {
128 size_t buf_len; 128 size_t buf_len;
129 struct i2c_adapter adapter; 129 struct i2c_adapter adapter;
130 unsigned rev1:1; 130 unsigned rev1:1;
131 unsigned idle:1;
132 u16 iestate; /* Saved interrupt register */
131}; 133};
132 134
133static inline void omap_i2c_write_reg(struct omap_i2c_dev *i2c_dev, 135static inline void omap_i2c_write_reg(struct omap_i2c_dev *i2c_dev,
@@ -174,18 +176,30 @@ static void omap_i2c_put_clocks(struct omap_i2c_dev *dev)
174 } 176 }
175} 177}
176 178
177static void omap_i2c_enable_clocks(struct omap_i2c_dev *dev) 179static void omap_i2c_unidle(struct omap_i2c_dev *dev)
178{ 180{
179 if (dev->iclk != NULL) 181 if (dev->iclk != NULL)
180 clk_enable(dev->iclk); 182 clk_enable(dev->iclk);
181 clk_enable(dev->fclk); 183 clk_enable(dev->fclk);
184 if (dev->iestate)
185 omap_i2c_write_reg(dev, OMAP_I2C_IE_REG, dev->iestate);
186 dev->idle = 0;
182} 187}
183 188
184static void omap_i2c_disable_clocks(struct omap_i2c_dev *dev) 189static void omap_i2c_idle(struct omap_i2c_dev *dev)
185{ 190{
191 u16 iv;
192
193 dev->idle = 1;
194 dev->iestate = omap_i2c_read_reg(dev, OMAP_I2C_IE_REG);
195 omap_i2c_write_reg(dev, OMAP_I2C_IE_REG, 0);
196 if (dev->rev1)
197 iv = omap_i2c_read_reg(dev, OMAP_I2C_IV_REG); /* Read clears */
198 else
199 omap_i2c_write_reg(dev, OMAP_I2C_STAT_REG, dev->iestate);
200 clk_disable(dev->fclk);
186 if (dev->iclk != NULL) 201 if (dev->iclk != NULL)
187 clk_disable(dev->iclk); 202 clk_disable(dev->iclk);
188 clk_disable(dev->fclk);
189} 203}
190 204
191static int omap_i2c_init(struct omap_i2c_dev *dev) 205static int omap_i2c_init(struct omap_i2c_dev *dev)
@@ -360,7 +374,7 @@ omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
360 int i; 374 int i;
361 int r; 375 int r;
362 376
363 omap_i2c_enable_clocks(dev); 377 omap_i2c_unidle(dev);
364 378
365 if ((r = omap_i2c_wait_for_bb(dev)) < 0) 379 if ((r = omap_i2c_wait_for_bb(dev)) < 0)
366 goto out; 380 goto out;
@@ -374,7 +388,7 @@ omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
374 if (r == 0) 388 if (r == 0)
375 r = num; 389 r = num;
376out: 390out:
377 omap_i2c_disable_clocks(dev); 391 omap_i2c_idle(dev);
378 return r; 392 return r;
379} 393}
380 394
@@ -403,6 +417,9 @@ omap_i2c_rev1_isr(int this_irq, void *dev_id)
403 struct omap_i2c_dev *dev = dev_id; 417 struct omap_i2c_dev *dev = dev_id;
404 u16 iv, w; 418 u16 iv, w;
405 419
420 if (dev->idle)
421 return IRQ_NONE;
422
406 iv = omap_i2c_read_reg(dev, OMAP_I2C_IV_REG); 423 iv = omap_i2c_read_reg(dev, OMAP_I2C_IV_REG);
407 switch (iv) { 424 switch (iv) {
408 case 0x00: /* None */ 425 case 0x00: /* None */
@@ -457,6 +474,9 @@ omap_i2c_isr(int this_irq, void *dev_id)
457 u16 stat, w; 474 u16 stat, w;
458 int count = 0; 475 int count = 0;
459 476
477 if (dev->idle)
478 return IRQ_NONE;
479
460 bits = omap_i2c_read_reg(dev, OMAP_I2C_IE_REG); 480 bits = omap_i2c_read_reg(dev, OMAP_I2C_IE_REG);
461 while ((stat = (omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG))) & bits) { 481 while ((stat = (omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG))) & bits) {
462 dev_dbg(dev->dev, "IRQ (ISR = 0x%04x)\n", stat); 482 dev_dbg(dev->dev, "IRQ (ISR = 0x%04x)\n", stat);
@@ -575,7 +595,7 @@ omap_i2c_probe(struct platform_device *pdev)
575 if ((r = omap_i2c_get_clocks(dev)) != 0) 595 if ((r = omap_i2c_get_clocks(dev)) != 0)
576 goto err_free_mem; 596 goto err_free_mem;
577 597
578 omap_i2c_enable_clocks(dev); 598 omap_i2c_unidle(dev);
579 599
580 if (cpu_is_omap15xx()) 600 if (cpu_is_omap15xx())
581 dev->rev1 = omap_i2c_read_reg(dev, OMAP_I2C_REV_REG) < 0x20; 601 dev->rev1 = omap_i2c_read_reg(dev, OMAP_I2C_REV_REG) < 0x20;
@@ -610,7 +630,7 @@ omap_i2c_probe(struct platform_device *pdev)
610 goto err_free_irq; 630 goto err_free_irq;
611 } 631 }
612 632
613 omap_i2c_disable_clocks(dev); 633 omap_i2c_idle(dev);
614 634
615 return 0; 635 return 0;
616 636
@@ -618,7 +638,7 @@ err_free_irq:
618 free_irq(dev->irq, dev); 638 free_irq(dev->irq, dev);
619err_unuse_clocks: 639err_unuse_clocks:
620 omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, 0); 640 omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, 0);
621 omap_i2c_disable_clocks(dev); 641 omap_i2c_idle(dev);
622 omap_i2c_put_clocks(dev); 642 omap_i2c_put_clocks(dev);
623err_free_mem: 643err_free_mem:
624 platform_set_drvdata(pdev, NULL); 644 platform_set_drvdata(pdev, NULL);