summaryrefslogtreecommitdiff
path: root/target/linux/s3c24xx/patches-2.6.26/0176-fix-pcf50633-suspend-state-as-enum.patch.patch
blob: 243710a4d377d03ee9085fd8689ebcb7d2af1935 (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
From 591a5d7e8da059de812d315b865fd5c0fa89071e Mon Sep 17 00:00:00 2001
From: Andy Green <andy@openmoko.com>
Date: Fri, 25 Jul 2008 23:06:15 +0100
Subject: [PATCH] fix-pcf50633-suspend-state-as-enum.patch

Use an enum to define pcf50633 suspend / resume state.
Add PCF50633_SS_RESUMING_BUT_NOT_US_YET to be the state
early in resume: add platform driver resume function just
to set this state so we can differentiate between early
resume and late suspend.

Signed-off-by: Andy Green <andy@openmoko.com>
---
 drivers/i2c/chips/pcf50633.c |   73 ++++++++++++++++++++++++++++++++++--------
 1 files changed, 59 insertions(+), 14 deletions(-)

diff --git a/drivers/i2c/chips/pcf50633.c b/drivers/i2c/chips/pcf50633.c
index 37dfca6..87a3003 100644
--- a/drivers/i2c/chips/pcf50633.c
+++ b/drivers/i2c/chips/pcf50633.c
@@ -108,6 +108,16 @@ enum charger_type {
 
 #define MAX_ADC_FIFO_DEPTH 8
 
+enum pcf50633_suspend_states {
+	PCF50633_SS_RUNNING,
+	PCF50633_SS_STARTING_SUSPEND,
+	PCF50633_SS_COMPLETED_SUSPEND,
+	PCF50633_SS_RESUMING_BUT_NOT_US_YET,
+	PCF50633_SS_STARTING_RESUME,
+	PCF50633_SS_COMPLETED_RESUME,
+};
+
+
 struct pcf50633_data {
 	struct i2c_client client;
 	struct pcf50633_platform_data *pdata;
@@ -122,9 +132,9 @@ struct pcf50633_data {
 	int allow_close;
 	int onkey_seconds;
 	int irq;
-	int have_been_suspended;
+	enum pcf50633_suspend_states suspend_state;
 	int usb_removal_count;
-	unsigned char pcfirq_resume[5];
+	u8 pcfirq_resume[5];
 	int probe_completed;
 
 	/* if he pulls battery while charging, we notice that and correctly
@@ -191,7 +201,7 @@ static struct platform_device *pcf50633_pdev;
 
 static int __reg_write(struct pcf50633_data *pcf, u_int8_t reg, u_int8_t val)
 {
-	if (pcf->have_been_suspended == 1) {
+	if (pcf->suspend_state == PCF50633_SS_COMPLETED_SUSPEND) {
 		dev_err(&pcf->client.dev, "__reg_write while suspended\n");
 		dump_stack();
 	}
@@ -213,7 +223,7 @@ static int32_t __reg_read(struct pcf50633_data *pcf, u_int8_t reg)
 {
 	int32_t ret;
 
-	if (pcf->have_been_suspended == 1) {
+	if (pcf->suspend_state == PCF50633_SS_COMPLETED_SUSPEND) {
 		dev_err(&pcf->client.dev, "__reg_read while suspended\n");
 		dump_stack();
 	}
@@ -630,7 +640,8 @@ static void pcf50633_work_usbcurlim(struct work_struct *work)
 	/* we got a notification from USB stack before we completed resume...
 	 * that can only make trouble, reschedule for a retry
 	 */
-	if (pcf->have_been_suspended && (pcf->have_been_suspended < 3))
+	if (pcf->suspend_state &&
+		     (pcf->suspend_state < PCF50633_SS_COMPLETED_RESUME))
 		goto reschedule;
 
 	/*
@@ -751,6 +762,21 @@ static void pcf50633_work(struct work_struct *work)
 	pcf->working = 1;
 
 	/*
+	 * if we are presently suspending, we are not in a position to deal
+	 * with pcf50633 interrupts at all.
+	 *
+	 * Because we didn't clear the int pending registers, there will be
+	 * no edge / interrupt waiting for us when we wake.  But it is OK
+	 * because at the end of our resume, we call this workqueue function
+	 * gratuitously, clearing the pending register and re-enabling
+	 * servicing this interrupt.
+	 */
+
+	if ((pcf->suspend_state == PCF50633_SS_STARTING_SUSPEND) ||
+	    (pcf->suspend_state == PCF50633_SS_COMPLETED_SUSPEND))
+		goto bail;
+
+	/*
 	 * If we are inside suspend -> resume completion time we don't attempt
 	 * service until we have fully resumed.  Although we could talk to the
 	 * device as soon as I2C is up, the regs in the device which we might
@@ -1154,8 +1180,9 @@ reschedule:
 	/* EXCEPTION: if we are in the middle of suspending, we don't have
 	 * time to hang around since we may be turned off core 1V3 already
 	 */
-	if (pcf->have_been_suspended != 1) {
-		msleep(50);
+	if ((pcf->suspend_state != PCF50633_SS_STARTING_SUSPEND) &&
+	    (pcf->suspend_state != PCF50633_SS_COMPLETED_SUSPEND)) {
+		msleep(10);
 		dev_info(&pcf->client.dev, "rescheduling interrupt service\n");
 	}
 	if (!schedule_work(&pcf->work))
@@ -2315,8 +2342,9 @@ static int pcf50633_suspend(struct device *dev, pm_message_t state)
 
 	/* we suspend once (!) as late as possible in the suspend sequencing */
 
-	if ((state.event != PM_EVENT_SUSPEND) || (pcf->have_been_suspended))
-		return 0;
+	if ((state.event != PM_EVENT_SUSPEND) ||
+	    (pcf->suspend_state != PCF50633_SS_RUNNING))
+		return -EBUSY;
 
 	/* The general idea is to power down all unused power supplies,
 	 * and then mask all PCF50633 interrupt sources but EXTONR, ONKEYF
@@ -2380,7 +2408,7 @@ static int pcf50633_suspend(struct device *dev, pm_message_t state)
 	if (ret)
 		dev_err(dev, "Failed to set wake masks :-( %d\n", ret);
 
-	pcf->have_been_suspended = 1;
+	pcf->suspend_state = PCF50633_SS_COMPLETED_SUSPEND;
 
 	mutex_unlock(&pcf->lock);
 
@@ -2393,7 +2421,8 @@ int pcf50633_ready(struct pcf50633_data *pcf)
 	if (!pcf)
 		return -EACCES;
 
-	if (pcf->have_been_suspended && (pcf->have_been_suspended < 3))
+	if ((pcf->suspend_state != PCF50633_SS_RUNNING) &&
+	    (pcf->suspend_state < PCF50633_SS_COMPLETED_RESUME))
 		return -EBUSY;
 
 	return 0;
@@ -2428,10 +2457,10 @@ static int pcf50633_resume(struct device *dev)
 	u8 res[5];
 
 	dev_info(dev, "pcf50633_resume suspended on entry = %d\n",
-						      pcf->have_been_suspended);
+						 (int)pcf->suspend_state);
 	mutex_lock(&pcf->lock);
 
-	pcf->have_been_suspended = 2; /* resuming */
+	pcf->suspend_state = PCF50633_SS_STARTING_RESUME;
 
 	/* these guys get reset while pcf50633 is suspend state, refresh */
 
@@ -2464,7 +2493,7 @@ static int pcf50633_resume(struct device *dev)
 	if (ret)
 		dev_err(dev, "Failed to set int masks :-( %d\n", ret);
 
-	pcf->have_been_suspended = 3; /* resume completed */
+	pcf->suspend_state = PCF50633_SS_COMPLETED_RESUME;
 
 	mutex_unlock(&pcf->lock);
 
@@ -2498,6 +2527,21 @@ static struct i2c_driver pcf50633_driver = {
 	.detach_client	= pcf50633_detach_client,
 };
 
+/* we have this purely to capture an early indication that we are coming out
+ * of suspend, before our device resume got called; async interrupt service is
+ * interested in this
+ */
+
+static int pcf50633_plat_resume(struct platform_device *pdev)
+{
+	/* i2c_get_clientdata(to_i2c_client(&pdev->dev)) returns NULL at this
+	 * early resume time so we have to use pcf50633_global
+	 */
+	pcf50633_global->suspend_state = PCF50633_SS_RESUMING_BUT_NOT_US_YET;
+
+	return 0;
+}
+
 /* platform driver, since i2c devices don't have platform_data */
 static int __init pcf50633_plat_probe(struct platform_device *pdev)
 {
@@ -2519,6 +2563,7 @@ static int pcf50633_plat_remove(struct platform_device *pdev)
 static struct platform_driver pcf50633_plat_driver = {
 	.probe	= pcf50633_plat_probe,
 	.remove	= pcf50633_plat_remove,
+	.resume_early = pcf50633_plat_resume,
 	.driver = {
 		.owner	= THIS_MODULE,
 		.name 	= "pcf50633",
-- 
1.5.6.3