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>

authored by Tony Lindgren and committed by Jean Delvare f08ac4e7 70849251

+28 -8
+28 -8
drivers/i2c/busses/i2c-omap.c
··· 128 128 size_t buf_len; 129 129 struct i2c_adapter adapter; 130 130 unsigned rev1:1; 131 + unsigned idle:1; 132 + u16 iestate; /* Saved interrupt register */ 131 133 }; 132 134 133 135 static inline void omap_i2c_write_reg(struct omap_i2c_dev *i2c_dev, ··· 176 174 } 177 175 } 178 176 179 - static void omap_i2c_enable_clocks(struct omap_i2c_dev *dev) 177 + static void omap_i2c_unidle(struct omap_i2c_dev *dev) 180 178 { 181 179 if (dev->iclk != NULL) 182 180 clk_enable(dev->iclk); 183 181 clk_enable(dev->fclk); 182 + if (dev->iestate) 183 + omap_i2c_write_reg(dev, OMAP_I2C_IE_REG, dev->iestate); 184 + dev->idle = 0; 184 185 } 185 186 186 - static void omap_i2c_disable_clocks(struct omap_i2c_dev *dev) 187 + static void omap_i2c_idle(struct omap_i2c_dev *dev) 187 188 { 189 + u16 iv; 190 + 191 + dev->idle = 1; 192 + dev->iestate = omap_i2c_read_reg(dev, OMAP_I2C_IE_REG); 193 + omap_i2c_write_reg(dev, OMAP_I2C_IE_REG, 0); 194 + if (dev->rev1) 195 + iv = omap_i2c_read_reg(dev, OMAP_I2C_IV_REG); /* Read clears */ 196 + else 197 + omap_i2c_write_reg(dev, OMAP_I2C_STAT_REG, dev->iestate); 198 + clk_disable(dev->fclk); 188 199 if (dev->iclk != NULL) 189 200 clk_disable(dev->iclk); 190 - clk_disable(dev->fclk); 191 201 } 192 202 193 203 static int omap_i2c_init(struct omap_i2c_dev *dev) ··· 374 360 int i; 375 361 int r; 376 362 377 - omap_i2c_enable_clocks(dev); 363 + omap_i2c_unidle(dev); 378 364 379 365 if ((r = omap_i2c_wait_for_bb(dev)) < 0) 380 366 goto out; ··· 388 374 if (r == 0) 389 375 r = num; 390 376 out: 391 - omap_i2c_disable_clocks(dev); 377 + omap_i2c_idle(dev); 392 378 return r; 393 379 } 394 380 ··· 416 402 { 417 403 struct omap_i2c_dev *dev = dev_id; 418 404 u16 iv, w; 405 + 406 + if (dev->idle) 407 + return IRQ_NONE; 419 408 420 409 iv = omap_i2c_read_reg(dev, OMAP_I2C_IV_REG); 421 410 switch (iv) { ··· 473 456 u16 bits; 474 457 u16 stat, w; 475 458 int count = 0; 459 + 460 + if (dev->idle) 461 + return IRQ_NONE; 476 462 477 463 bits = omap_i2c_read_reg(dev, OMAP_I2C_IE_REG); 478 464 while ((stat = (omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG))) & bits) { ··· 595 575 if ((r = omap_i2c_get_clocks(dev)) != 0) 596 576 goto err_free_mem; 597 577 598 - omap_i2c_enable_clocks(dev); 578 + omap_i2c_unidle(dev); 599 579 600 580 if (cpu_is_omap15xx()) 601 581 dev->rev1 = omap_i2c_read_reg(dev, OMAP_I2C_REV_REG) < 0x20; ··· 630 610 goto err_free_irq; 631 611 } 632 612 633 - omap_i2c_disable_clocks(dev); 613 + omap_i2c_idle(dev); 634 614 635 615 return 0; 636 616 ··· 638 618 free_irq(dev->irq, dev); 639 619 err_unuse_clocks: 640 620 omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, 0); 641 - omap_i2c_disable_clocks(dev); 621 + omap_i2c_idle(dev); 642 622 omap_i2c_put_clocks(dev); 643 623 err_free_mem: 644 624 platform_set_drvdata(pdev, NULL);