diff options
Diffstat (limited to 'target/linux/s3c24xx/patches-2.6.26/1003-fix-i2c-s3c2410-resume-race.patch.patch')
-rwxr-xr-x | target/linux/s3c24xx/patches-2.6.26/1003-fix-i2c-s3c2410-resume-race.patch.patch | 106 |
1 files changed, 106 insertions, 0 deletions
diff --git a/target/linux/s3c24xx/patches-2.6.26/1003-fix-i2c-s3c2410-resume-race.patch.patch b/target/linux/s3c24xx/patches-2.6.26/1003-fix-i2c-s3c2410-resume-race.patch.patch new file mode 100755 index 0000000..77e6e9c --- /dev/null +++ b/target/linux/s3c24xx/patches-2.6.26/1003-fix-i2c-s3c2410-resume-race.patch.patch @@ -0,0 +1,106 @@ +From eb5cadc7ff0e6c3aedfec3323146d7bb6bdfe0f0 Mon Sep 17 00:00:00 2001 +From: mokopatches <mokopatches@openmoko.org> +Date: Wed, 16 Jul 2008 14:44:10 +0100 +Subject: [PATCH] fix-i2c-s3c2410-resume-race.patch + fix-i2c-s3c2410-resume-race.patch + +There is a nasty race between i2c-s3c2410 resume and resume of I2C +driver and the client drivers -- the watchdog device actually gets to +use the dead I2C bus before it is reinitialized by the I2C driver +resume! This patch makes sure any customers get turned away until +the shopkeeper has woken up. + +Signed-off-by: Andy Green <andy@openmoko.com> +--- + drivers/i2c/busses/i2c-s3c2410.c | 33 +++++++++++++++++++++++++++++++++ + 1 files changed, 33 insertions(+), 0 deletions(-) + +diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c +index 9e8c875..02459d7 100644 +--- a/drivers/i2c/busses/i2c-s3c2410.c ++++ b/drivers/i2c/busses/i2c-s3c2410.c +@@ -71,6 +71,8 @@ struct s3c24xx_i2c { + struct resource *irq; + struct resource *ioarea; + struct i2c_adapter adap; ++ ++ int suspended; + }; + + /* default platform data to use if not supplied in the platform_device +@@ -156,6 +158,14 @@ static inline void s3c24xx_i2c_disable_irq(struct s3c24xx_i2c *i2c) + unsigned long tmp; + + tmp = readl(i2c->regs + S3C2410_IICCON); ++ ++/* S3c2442 datasheet ++ * ++ * If the IICCON[5]=0, IICCON[4] does not operate correctly. ++ * So, It is recommended that you should set IICCON[5]=1, ++ * although you does not use the IIC interrupt. ++ */ ++ + writel(tmp & ~S3C2410_IICCON_IRQEN, i2c->regs + S3C2410_IICCON); + } + +@@ -501,6 +511,14 @@ static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c, struct i2c_msg *msgs, int + unsigned long timeout; + int ret; + ++ if (i2c->suspended) { ++ dev_err(i2c->dev, ++ "Hey I am still asleep (suspended: %d), retry later\n", ++ i2c->suspended); ++ ret = -EAGAIN; ++ goto out; ++ } ++ + ret = s3c24xx_i2c_set_master(i2c); + if (ret != 0) { + dev_err(i2c->dev, "cannot get bus (error %d)\n", ret); +@@ -885,6 +903,17 @@ static int s3c24xx_i2c_remove(struct platform_device *pdev) + } + + #ifdef CONFIG_PM ++ ++static int s3c24xx_i2c_suspend(struct platform_device *dev, pm_message_t state) ++{ ++ struct s3c24xx_i2c *i2c = platform_get_drvdata(dev); ++ ++ if (i2c != NULL) ++ i2c->suspended++; ++ ++ return 0; ++} ++ + static int s3c24xx_i2c_resume(struct platform_device *dev) + { + struct s3c24xx_i2c *i2c = platform_get_drvdata(dev); +@@ -892,6 +921,8 @@ static int s3c24xx_i2c_resume(struct platform_device *dev) + if (i2c != NULL) + s3c24xx_i2c_init(i2c); + ++ i2c->suspended--; ++ + return 0; + } + +@@ -904,6 +935,7 @@ static int s3c24xx_i2c_resume(struct platform_device *dev) + static struct platform_driver s3c2410_i2c_driver = { + .probe = s3c24xx_i2c_probe, + .remove = s3c24xx_i2c_remove, ++ .suspend = s3c24xx_i2c_suspend, + .resume = s3c24xx_i2c_resume, + .driver = { + .owner = THIS_MODULE, +@@ -914,6 +946,7 @@ static struct platform_driver s3c2410_i2c_driver = { + static struct platform_driver s3c2440_i2c_driver = { + .probe = s3c24xx_i2c_probe, + .remove = s3c24xx_i2c_remove, ++ .suspend = s3c24xx_i2c_suspend, + .resume = s3c24xx_i2c_resume, + .driver = { + .owner = THIS_MODULE, +-- +1.5.6.3 + |