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