summaryrefslogtreecommitdiff
path: root/target/linux/s3c24xx/patches-2.6.26/1159-fix-pcf50633-suspend-resume-onehit-i2c-other-meddlin.patch
blob: e7fc674f1c94117e96f9d4cb06ba592685975812 (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
From 286581f7a1cd2536b70cc0cfbc742ee45260252e Mon Sep 17 00:00:00 2001
From: Andy Green <andy@openmoko.com>
Date: Fri, 25 Jul 2008 23:06:12 +0100
Subject: [PATCH] fix-pcf50633-suspend-resume-onehit-i2c-other-meddling.patch

 - speed up suspend and resume by using one hit i2c bulk transactions
 - don't bother storing int mask set on suspend, the default one is
   what we use anyway
 - put stack_trace() on pcf50633 low level access that fire if we
   try to touch them before we resumed
 - cosmetic source cleanup
 - reduces resume time for pcf50633 from 450ms to 255ms

Signed-off-by: Andy Green <andy@openmoko.com>
---
 arch/arm/mach-s3c2440/mach-gta02.c |   21 ++-------
 drivers/i2c/chips/pcf50633.c       |   83 +++++++++++++++++++----------------
 2 files changed, 49 insertions(+), 55 deletions(-)

diff --git a/arch/arm/mach-s3c2440/mach-gta02.c b/arch/arm/mach-s3c2440/mach-gta02.c
index 9ba1036..22de181 100644
--- a/arch/arm/mach-s3c2440/mach-gta02.c
+++ b/arch/arm/mach-s3c2440/mach-gta02.c
@@ -477,24 +477,11 @@ static struct pcf50633_platform_data gta02_pcf_pdata = {
 	.r_fix_batt_par	= 10000,
 	.r_sense_milli	= 220,
 	.resumers = {
-		[0] = /* PCF50633_INT1_ADPINS 	| */
-		  /* PCF50633_INT1_ADPREM	| */
-		  PCF50633_INT1_USBINS		|
-		  PCF50633_INT1_USBREM		|
-		  PCF50633_INT1_ALARM,
+		[0] = PCF50633_INT1_USBINS |
+		      PCF50633_INT1_USBREM |
+		      PCF50633_INT1_ALARM,
 		[1] = PCF50633_INT2_ONKEYF,
-		[2] =  /* PCF50633_INT3_BATFULL	| */
-		  /* PCF50633_INT3_CHGHALT	| */
-		  /* PCF50633_INT3_THLIMON	| */
-		  /* PCF50633_INT3_THLIMOFF	| */
-		  /* PCF50633_INT3_USBLIMON	| */
-		  /* PCF50633_INT3_USBLIMOFF	| */
-		  PCF50633_INT3_ONKEY1S ,
-		[3] = 0				/* |
-		     PCF50633_INT4_LOWSYS	| */
-		  /* PCF50633_INT4_LOWBAT	| */
-		  /* PCF50633_INT4_HIGHTMP */,
-		[4] = 0
+		[2] = PCF50633_INT3_ONKEY1S
 	},
 	.rails	= {
 		[PCF50633_REGULATOR_AUTO] = {
diff --git a/drivers/i2c/chips/pcf50633.c b/drivers/i2c/chips/pcf50633.c
index f6886d7..c148ea7 100644
--- a/drivers/i2c/chips/pcf50633.c
+++ b/drivers/i2c/chips/pcf50633.c
@@ -163,10 +163,7 @@ struct pcf50633_data {
 		u_int8_t down2out, down2ena;
 		u_int8_t memldoout, memldoena;
 		u_int8_t ledout, ledena, leddim;
-		struct {
-			u_int8_t out;
-			u_int8_t ena;
-		} ldo[__NUM_PCF50633_REGS];
+		u_int8_t ldo[__NUM_PCF50633_REGS][2];
 	} standby_regs;
 
 	struct resume_dependency resume_dependency;
@@ -187,6 +184,10 @@ 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) {
+		dev_err(&pcf->client.dev, "__reg_write while suspended\n");
+		dump_stack();
+	}
 	return i2c_smbus_write_byte_data(&pcf->client, reg, val);
 }
 
@@ -205,6 +206,10 @@ static int32_t __reg_read(struct pcf50633_data *pcf, u_int8_t reg)
 {
 	int32_t ret;
 
+	if (pcf->have_been_suspended == 1) {
+		dev_err(&pcf->client.dev, "__reg_read while suspended\n");
+		dump_stack();
+	}
 	ret = i2c_smbus_read_byte_data(&pcf->client, reg);
 
 	return ret;
@@ -2155,6 +2160,13 @@ static int pcf50633_suspend(struct device *dev, pm_message_t state)
 	struct i2c_client *client = to_i2c_client(dev);
 	struct pcf50633_data *pcf = i2c_get_clientdata(client);
 	int i;
+	int ret;
+	u_int8_t tmp;
+
+	/* we suspend once (!) as late as possible in the suspend sequencing */
+
+	if ((state.event != PM_EVENT_SUSPEND) || (pcf->have_been_suspended))
+		return 0;
 
 	/* The general idea is to power down all unused power supplies,
 	 * and then mask all PCF50606 interrup sources but EXTONR, ONKEYF
@@ -2176,25 +2188,25 @@ static int pcf50633_suspend(struct device *dev, pm_message_t state)
 	pcf->standby_regs.ledout = __reg_read(pcf, PCF50633_REG_LEDOUT);
 	pcf->standby_regs.ledena = __reg_read(pcf, PCF50633_REG_LEDENA);
 	pcf->standby_regs.leddim = __reg_read(pcf, PCF50633_REG_LEDDIM);
-	/* FIXME: one big read? */
-	for (i = 0; i < 7; i++) {
-		u_int8_t reg_out = PCF50633_REG_LDO1OUT + 2*i;
-		pcf->standby_regs.ldo[i].out = __reg_read(pcf, reg_out);
-		pcf->standby_regs.ldo[i].ena = __reg_read(pcf, reg_out+1);
-	}
+
+	/* regulator voltages and enable states */
+	ret = i2c_smbus_read_i2c_block_data(&pcf->client,
+					    PCF50633_REG_LDO1OUT, 14,
+					    &pcf->standby_regs.ldo[0][0]);
+	if (ret != 14)
+		dev_err(dev, "Failed to save LDO levels and enables :-(\n");
 
 	/* switch off power supplies that are not needed during suspend */
 	for (i = 0; i < __NUM_PCF50633_REGULATORS; i++) {
-		if (!(pcf->pdata->rails[i].flags & PMU_VRAIL_F_SUSPEND_ON)) {
-			u_int8_t tmp;
-
-			DEBUGP("disabling pcf50633 regulator %u\n", i);
-			/* we cannot use pcf50633_onoff_set() because we're
-			 * already under the mutex */
-			tmp = __reg_read(pcf, regulator_registers[i]+1);
-			tmp &= 0xfe;
-			__reg_write(pcf, regulator_registers[i]+1, tmp);
-		}
+		if ((pcf->pdata->rails[i].flags & PMU_VRAIL_F_SUSPEND_ON))
+			continue;
+
+		dev_dbg(dev, "disabling regulator %u\n", i);
+		/* we cannot use pcf50633_onoff_set() because we're
+		 * already under the mutex */
+		tmp = __reg_read(pcf, regulator_registers[i]+1);
+		tmp &= 0xfe;
+		__reg_write(pcf, regulator_registers[i]+1, tmp);
 	}
 
 	/* turn off the backlight */
@@ -2202,11 +2214,9 @@ static int pcf50633_suspend(struct device *dev, pm_message_t state)
 	__reg_write(pcf, PCF50633_REG_LEDOUT, 2);
 	__reg_write(pcf, PCF50633_REG_LEDENA, 0x00);
 
-	pcf->standby_regs.int1m = __reg_read(pcf, PCF50633_REG_INT1M);
-	pcf->standby_regs.int2m = __reg_read(pcf, PCF50633_REG_INT2M);
-	pcf->standby_regs.int3m = __reg_read(pcf, PCF50633_REG_INT3M);
-	pcf->standby_regs.int4m = __reg_read(pcf, PCF50633_REG_INT4M);
-	pcf->standby_regs.int5m = __reg_read(pcf, PCF50633_REG_INT5M);
+	/* set interrupt masks so only those sources we want to wake
+	 * us are able to
+	 */
 	__reg_write(pcf, PCF50633_REG_INT1M, ~pcf->pdata->resumers[0]);
 	__reg_write(pcf, PCF50633_REG_INT2M, ~pcf->pdata->resumers[1]);
 	__reg_write(pcf, PCF50633_REG_INT3M, ~pcf->pdata->resumers[2]);
@@ -2240,16 +2250,13 @@ static int pcf50633_resume(struct device *dev)
 {
 	struct i2c_client *client = to_i2c_client(dev);
 	struct pcf50633_data *pcf = i2c_get_clientdata(client);
-	int i;
+	int ret;
 
 	mutex_lock(&pcf->lock);
 
-	/* Resume all saved registers that don't "survive" standby state */
-	__reg_write(pcf, PCF50633_REG_INT1M, pcf->standby_regs.int1m);
-	__reg_write(pcf, PCF50633_REG_INT2M, pcf->standby_regs.int2m);
-	__reg_write(pcf, PCF50633_REG_INT3M, pcf->standby_regs.int3m);
-	__reg_write(pcf, PCF50633_REG_INT4M, pcf->standby_regs.int4m);
-	__reg_write(pcf, PCF50633_REG_INT5M, pcf->standby_regs.int5m);
+	pcf->have_been_suspended = 2; /* resuming */
+
+	/* these guys get reset while pcf50633 is suspend state, refresh */
 
 	__reg_write(pcf, PCF50633_REG_OOCTIM2, pcf->standby_regs.ooctim2);
 	__reg_write(pcf, PCF50633_REG_AUTOOUT, pcf->standby_regs.autoout);
@@ -2265,12 +2272,12 @@ static int pcf50633_resume(struct device *dev)
 	if (!pcf->pdata->defer_resume_backlight)
 		pcf50633_backlight_resume(pcf);
 
-	/* FIXME: one big read? */
-	for (i = 0; i < 7; i++) {
-		u_int8_t reg_out = PCF50633_REG_LDO1OUT + 2*i;
-		__reg_write(pcf, reg_out, pcf->standby_regs.ldo[i].out);
-		__reg_write(pcf, reg_out+1, pcf->standby_regs.ldo[i].ena);
-	}
+	/* regulator voltages and enable states */
+	ret = i2c_smbus_write_i2c_block_data(&pcf->client,
+					    PCF50633_REG_LDO1OUT, 14,
+					    &pcf->standby_regs.ldo[0][0]);
+	if (ret)
+		dev_err(dev, "Failed to restore LDOs :-( %d\n", ret);
 
 	mutex_unlock(&pcf->lock);
 
-- 
1.5.6.3