aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/s3c24xx/patches-2.6.26/0003-fix-i2c-s3c2410-resume-race.patch.patch
blob: 77e6e9c958e73f4a4738f8cd58689bbb030b0510 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
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