summaryrefslogtreecommitdiffstats
path: root/target/linux/s3c24xx/patches-2.6.24/1271-always-call-resume-dependencies.patch.patch
blob: f484734563a00770432b6fbe27ec94e839d87094 (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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
From 36879992de8774cbf8686740bbda383cc6fbdcbb Mon Sep 17 00:00:00 2001
From: \\\\\\\"Mike (mwester)\\\\\\ <mwester@dls.net>
Date: Mon, 11 Aug 2008 20:16:09 +0100
Subject: [PATCH] always-call-resume-dependencies.patch

Attached is a patch that has greatly reduced the frequency of failures
to resume (due to an oops from the glamo resume handler), and the
dreaded "white screen after resume".  I can't say that it fixes all of
these, although I have yet to see the white-screen since applying this
patch and suspending/resuming several hundred times (with the 30-second
suspend on the 2008.8 image and the endless stream of GSM error messages
generated by something in that image, it has proved to be very useful to
do an automated stress test!)

This patch will apply to stable, and should make stable slightly more,
well, "stable".

[Feel free to remove the debug messages if someone feels strongly about
that; I left them in because I think they might be useful in triaging
further crashes; I'm not at all convinced that this patch will fix all
the cases of resume failures.]

[[And, yes, this is ugly, really ugly.]]

[[[Oh yeah - there's still one extreme case that will result in an oops:
 if a dependent driver is built as a module, and it is unloaded, and it
happened that the preceding suspend/resume was aborted, and that abort
happened between the dependent driver and the driver upon which it is
dependent, then a list entry will be left behind referencing the
unloaded module.  There's just no good way to fix that given the way the
resume dependency plumbing is connected up right now, so just avoid
using modules for any of the drivers involved in the resume dependency
stuff.]]]

Regards,
Mike (mwester)
commit 905d2fc9c45f622418ce9ef4e67c23453aab7571
Author: Mike Westerhof <mwester@dls.net>
Date:   Mon Aug 11 11:11:25 2008 -0500

    always-call-resume-dependencies.patch

    Ensure that a dependent resume handler is always executed,
    even if the resume handler for driver upon which it is
    dependent never suspends (and therefore never resumes either).
    Also make sure that we do not end up with duplicate
    dependencies registered, something that can happen if the
    suspend is aborted due to driver failure or an early resume
    (such as occurs when the GSM interrupts during suspend).

    Signed-off-by: Mike Westerhof <mwester@dls.net>
---
 drivers/i2c/chips/pcf50633.c      |    6 +++++
 drivers/mfd/glamo/glamo-core.c    |    5 ++++
 drivers/mfd/glamo/glamo-core.h    |    1 +
 drivers/serial/s3c2410.c          |    7 ++++++
 include/linux/resume-dependency.h |   38 ++++++++++++++++++++++++++++++++++++-
 5 files changed, 56 insertions(+), 1 deletions(-)

diff --git a/drivers/i2c/chips/pcf50633.c b/drivers/i2c/chips/pcf50633.c
index 98c254c..b90ea8c 100644
--- a/drivers/i2c/chips/pcf50633.c
+++ b/drivers/i2c/chips/pcf50633.c
@@ -183,6 +183,7 @@ struct pcf50633_data {
 	} standby_regs;
 
 	struct resume_dependency resume_dependency;
+	int is_suspended;
 
 #endif
 };
@@ -2366,6 +2367,8 @@ void pcf50633_register_resume_dependency(struct pcf50633_data *pcf,
 					struct resume_dependency *dep)
 {
 	register_resume_dependency(&pcf->resume_dependency, dep);
+	if (pcf->is_suspended)
+		activate_all_resume_dependencies(&pcf->resume_dependency);
 }
 EXPORT_SYMBOL_GPL(pcf50633_register_resume_dependency);
 
@@ -2462,6 +2465,8 @@ static int pcf50633_suspend(struct device *dev, pm_message_t state)
 
 	mutex_unlock(&pcf->lock);
 
+	pcf->is_suspended = 1;
+	activate_all_resume_dependencies(&pcf->resume_dependency);
 	return 0;
 }
 
@@ -2590,6 +2595,7 @@ static int pcf50633_resume(struct device *dev)
 	get_device(&pcf->client.dev);
 	pcf50633_work(&pcf->work);
 
+	pcf->is_suspended = 0;
 	callback_all_resume_dependencies(&pcf->resume_dependency);
 
 	return 0;
diff --git a/drivers/mfd/glamo/glamo-core.c b/drivers/mfd/glamo/glamo-core.c
index c094a8c..e310382 100644
--- a/drivers/mfd/glamo/glamo-core.c
+++ b/drivers/mfd/glamo/glamo-core.c
@@ -1248,6 +1248,8 @@ void glamo_register_resume_dependency(struct resume_dependency *
 {
 	register_resume_dependency(&glamo_handle->resume_dependency,
 							     resume_dependency);
+	if (glamo_handle->is_suspended)
+		activate_all_resume_dependencies(&glamo_handle->resume_dependency);
 }
 EXPORT_SYMBOL_GPL(glamo_register_resume_dependency);
 
@@ -1255,12 +1257,15 @@ EXPORT_SYMBOL_GPL(glamo_register_resume_dependency);
 static int glamo_suspend(struct platform_device *pdev, pm_message_t state)
 {
 	glamo_power(glamo_handle, GLAMO_POWER_SUSPEND);
+	glamo_handle->is_suspended = 1;
+	activate_all_resume_dependencies(&glamo_handle->resume_dependency);
 	return 0;
 }
 
 static int glamo_resume(struct platform_device *pdev)
 {
 	glamo_power(glamo_handle, GLAMO_POWER_ON);
+	glamo_handle->is_suspended = 0;
 	callback_all_resume_dependencies(&glamo_handle->resume_dependency);
 
 	return 0;
diff --git a/drivers/mfd/glamo/glamo-core.h b/drivers/mfd/glamo/glamo-core.h
index d3f4309..ac5eacf 100644
--- a/drivers/mfd/glamo/glamo-core.h
+++ b/drivers/mfd/glamo/glamo-core.h
@@ -32,6 +32,7 @@ struct glamo_core {
 	struct resume_dependency resume_dependency;
 	u32 engine_enabled_bitfield;
 	u32 engine_enabled_bitfield_suspend;
+	int is_suspended;
 };
 
 struct glamo_script {
diff --git a/drivers/serial/s3c2410.c b/drivers/serial/s3c2410.c
index 2a388cf..3b1c07b 100644
--- a/drivers/serial/s3c2410.c
+++ b/drivers/serial/s3c2410.c
@@ -116,6 +116,7 @@ struct s3c24xx_uart_port {
 	struct uart_port		port;
 
 	struct resume_dependency	resume_dependency;
+	int				is_suspended;
 };
 
 
@@ -1188,10 +1189,13 @@ static int s3c24xx_serial_remove(struct platform_device *dev)
 static int s3c24xx_serial_suspend(struct platform_device *dev, pm_message_t state)
 {
 	struct uart_port *port = s3c24xx_dev_to_port(&dev->dev);
+	struct s3c24xx_uart_port *ourport = to_ourport(port);
 
 	if (port)
 		uart_suspend_port(&s3c24xx_uart_drv, port);
 
+	activate_all_resume_dependencies(&ourport->resume_dependency);
+	ourport->is_suspended = 1;
 	return 0;
 }
 
@@ -1202,6 +1206,8 @@ void s3c24xx_serial_register_resume_dependency(struct resume_dependency *
 
 	register_resume_dependency(&ourport->resume_dependency,
 							     resume_dependency);
+	if (ourport->is_suspended)
+		activate_all_resume_dependencies(&ourport->resume_dependency);
 }
 EXPORT_SYMBOL(s3c24xx_serial_register_resume_dependency);
 
@@ -1218,6 +1224,7 @@ static int s3c24xx_serial_resume(struct platform_device *dev)
 		uart_resume_port(&s3c24xx_uart_drv, port);
 	}
 
+	ourport->is_suspended = 0;
 	callback_all_resume_dependencies(&ourport->resume_dependency);
 
 	return 0;
diff --git a/include/linux/resume-dependency.h b/include/linux/resume-dependency.h
index e0c0f33..959cadd 100644
--- a/include/linux/resume-dependency.h
+++ b/include/linux/resume-dependency.h
@@ -38,6 +38,7 @@ struct resume_dependency {
  */
 
 #define init_resume_dependency_list(_head) \
+	printk(KERN_ERR "##### init_resume_dependency_list(head=%p)\n", (_head)); \
 	INIT_LIST_HEAD(&(_head)->list);
 
 
@@ -48,7 +49,18 @@ struct resume_dependency {
  */
 
 #define register_resume_dependency(_head, _dep) { \
-	(_dep)->called_flag = 0; \
+	struct list_head *_pos, *_q; \
+	struct resume_dependency *_d; \
+\
+	printk(KERN_ERR "##### register_resume_dependency(head=%p, dep=%p)\n", (_head), (_dep)); \
+	(_dep)->called_flag = 1; \
+	list_for_each_safe(_pos, _q, &((_head)->list)) { \
+		_d = list_entry(_pos, struct resume_dependency, list); \
+		if (_d == (_dep)) { \
+			list_del(_pos); \
+			printk(KERN_ERR "#####   duplicate dependency removed first\n"); \
+		} \
+	} \
 	list_add(&(_dep)->list, &(_head)->list); \
 }
 
@@ -61,14 +73,38 @@ struct resume_dependency {
 	struct list_head *_pos, *_q; \
 	struct resume_dependency *_dep; \
 \
+	printk(KERN_ERR "##### callback_all_resume_dependencies(head=%p)\n", (_head)); \
 	list_for_each_safe(_pos, _q, &((_head)->list)) { \
 		_dep = list_entry(_pos, struct resume_dependency, list); \
+		printk(KERN_ERR "#####   callback list entry (head=%p, dep=%p)\n", (_head), (_dep)); \
 		_dep->called_flag = 1; \
+		printk(KERN_ERR "#####      callback=%p(context=%p))\n", (_dep->callback),(_dep->context)); \
 		(_dep->callback)(_dep->context); \
 		list_del(_pos); \
 	} \
 }
 
+/* When a dependency is added, it is not actually active; the dependent resume
+ * handler will function as normal.  The dependency is activated by the suspend
+ * handler for the driver that will be doing the callbacks.  This ensures that
+ * if the suspend is aborted for any reason (error, driver busy, etc), that all
+ * suspended drivers will resume, even if the driver upon which they are dependent
+ * did not suspend, and hence will not resume, and thus would be unable to perform
+ * the callbacks.
+ */
+
+#define activate_all_resume_dependencies(_head) { \
+	struct list_head *_pos, *_q; \
+	struct resume_dependency *_dep; \
+\
+	printk(KERN_ERR "##### activate_all_resume_dependencies(head=%p)\n", (_head)); \
+	list_for_each_safe(_pos, _q, &((_head)->list)) { \
+		_dep = list_entry(_pos, struct resume_dependency, list); \
+		printk(KERN_ERR "#####   activating callback list entry (head=%p, dep=%p)\n", (_head), (_dep)); \
+		_dep->called_flag = 0; \
+	} \
+}
+
 /* if your resume action is dependent on multiple drivers being resumed already,
  * register the same callback with each driver you are dependent on, and check
  * .called_flag for all of the struct resume_dependency.  When they are all 1
-- 
1.5.6.5