aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/i2c
diff options
context:
space:
mode:
authorJP Abgrall <jpa@google.com>2012-06-20 15:30:30 -0700
committerJP Abgrall <jpa@google.com>2012-07-25 10:11:22 -0700
commit751de25388809d3b590af681584160f38fd930dd (patch)
tree950c66780628ff514b9366e37b5fed47214183e3 /drivers/i2c
parent3b0c5d2887fca99cab7dd506817b1049d38198a1 (diff)
downloadkernel_samsung_aries-751de25388809d3b590af681584160f38fd930dd.zip
kernel_samsung_aries-751de25388809d3b590af681584160f38fd930dd.tar.gz
kernel_samsung_aries-751de25388809d3b590af681584160f38fd930dd.tar.bz2
i2c-s3c2410: prevent mem corruption after i2c timeout
This is a merge of backported changes from kernel/exynos android-exynos-3.4 -- commit 62cb87fd5b32f8cf07bc46a691318f4aa2cfc07d i2c-s3c2410: clean up i2c irq after timeout Otherwise, later irq handler will try to access freed memory region and may crash. Change-Id: I0ab9157b6f0d48d3a413e59a27641e88cd9db2eb Signed-off-by: Jinhee Hyeon <jh0722.hyen@samsung.com> Conflicts: drivers/i2c/busses/i2c-s3c2410.c -- commit cd668911107bd1432047406e31f3be7278dc4046 i2c-s3c2410: add missing stop sequence for I2C transfer To clearly terminate the connection, clear pending bit and serial out disable settings are added Change-Id: Ic5ccd80941a724f63ed7f22e777408725c5414ee Signed-off-by: HoJune Byun <hjune.byun@samsung.com> -- commit 6aeac78519c91d50757dc492ff12fbabc4cc756b i2c-s3c2410: I2C connection termination forcely To forcely terminate the connection, Serial Out bit is disabled. In case of timeout, or HDMIPHY bug Change-Id: I29c9ef2b8c35c7c970cbef4d5a856e178794de6e Signed-off-by: HoJune Byun <hjune.byun@samsung.com> -- commit 2951f354549bf65cf9a4733565841553fbc8a964 i2c-s3c2410: added STATE_STOP checking codes To prevent duplicated stop execution, added STATE_STOP checking codes before s3c24xx_i2c_stop function. Change-Id: I29417d4dbf94280e1d629338e85e54ad79e6be89 Signed-off-by: Taekgyun Ko <taeggyun.ko@samsung.com> Conflicts: drivers/i2c/busses/i2c-s3c2410.c Change-Id: I78ea9b014ed9656d6b5c4628d36bc8edd668b8e0 Signed-off-by: JP Abgrall <jpa@google.com>
Diffstat (limited to 'drivers/i2c')
-rw-r--r--drivers/i2c/busses/i2c-s3c2410.c53
1 files changed, 48 insertions, 5 deletions
diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c
index b723b0b..61c9365 100644
--- a/drivers/i2c/busses/i2c-s3c2410.c
+++ b/drivers/i2c/busses/i2c-s3c2410.c
@@ -201,18 +201,29 @@ static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c,
static inline void s3c24xx_i2c_stop(struct s3c24xx_i2c *i2c, int ret)
{
- unsigned long iicstat = readl(i2c->regs + S3C2410_IICSTAT);
+ unsigned long iicstat;
+ unsigned long iiccon;
dev_dbg(i2c->dev, "STOP\n");
/* stop the transfer */
+
+ /* Disable irq */
+ s3c24xx_i2c_disable_irq(i2c);
+
+ /* STOP signal generation : MTx(0xD0) */
+ iicstat = readl(i2c->regs + S3C2410_IICSTAT);
iicstat &= ~S3C2410_IICSTAT_START;
writel(iicstat, i2c->regs + S3C2410_IICSTAT);
- i2c->state = STATE_STOP;
+ /* Clear pending bit */
+ iiccon = readl(i2c->regs + S3C2410_IICCON);
+ iiccon &= ~S3C2410_IICCON_IRQPEND;
+ writel(iiccon, i2c->regs + S3C2410_IICCON);
s3c24xx_i2c_master_complete(i2c, ret);
- s3c24xx_i2c_disable_irq(i2c);
+
+ i2c->state = STATE_STOP;
}
/* helper functions to determine the current state in the set of
@@ -484,7 +495,8 @@ static int s3c24xx_i2c_set_master(struct s3c24xx_i2c *i2c)
static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c,
struct i2c_msg *msgs, int num)
{
- unsigned long timeout;
+ unsigned long iicstat, timeout;
+ int spins = 20;
int ret;
if (i2c->suspended)
@@ -523,7 +535,38 @@ static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c,
/* ensure the stop has been through the bus */
- udelay(10);
+ dev_dbg(i2c->dev, "waiting for bus idle\n");
+
+ /* first, try busy waiting briefly */
+ do {
+ cpu_relax();
+ iicstat = readl(i2c->regs + S3C2410_IICSTAT);
+ } while ((iicstat & S3C2410_IICSTAT_START) && --spins);
+
+ /* if that timed out sleep */
+ if (!spins) {
+ msleep(1);
+ iicstat = readl(i2c->regs + S3C2410_IICSTAT);
+ }
+
+ /* if still not finished, clean it up */
+ spin_lock_irq(&i2c->lock);
+
+ if (iicstat & S3C2410_IICSTAT_BUSBUSY) {
+ dev_dbg(i2c->dev, "timeout waiting for bus idle\n");
+
+ if (i2c->state != STATE_STOP) {
+ dev_dbg(i2c->dev,
+ "timeout : i2c interrupt hasn't occurred\n");
+ s3c24xx_i2c_stop(i2c, 0);
+ }
+
+ /* Disable Serial Out : To forcely terminate the connection */
+ iicstat = readl(i2c->regs + S3C2410_IICSTAT);
+ iicstat &= ~S3C2410_IICSTAT_TXRXEN;
+ writel(iicstat, i2c->regs + S3C2410_IICSTAT);
+ }
+ spin_unlock_irq(&i2c->lock);
out:
return ret;