1 From 0c136b451381e760c056a7683b7fc28901f54da4 Mon Sep 17 00:00:00 2001
2 From: Mike Westerhof <mwester@dls.net>
3 Date: Fri, 8 Aug 2008 13:10:59 +0100
4 Subject: [PATCH] gta01-pcf50606-disable-irq-from-suspend-until-resume.patch
6 This patch is the pcf50606 equivalent of the pcf50633 patch that
7 disables interrupts from the chip until after resume is complete.
8 In order to ensure no data is lost, the work function is called
9 post-resume to process any pending interrupts.
11 Most of the code was quite literally re-used from Andy Green's
14 Signed-off-by: Mike Westerhof <mwester@dls.net>
16 drivers/i2c/chips/pcf50606.c | 148 ++++++++++++++++++++++++++++++++++++++++--
17 1 files changed, 142 insertions(+), 6 deletions(-)
19 diff --git a/drivers/i2c/chips/pcf50606.c b/drivers/i2c/chips/pcf50606.c
20 index ddba1c7..18263fb 100644
21 --- a/drivers/i2c/chips/pcf50606.c
22 +++ b/drivers/i2c/chips/pcf50606.c
24 #include <linux/interrupt.h>
25 #include <linux/irq.h>
26 #include <linux/workqueue.h>
27 +#include <linux/delay.h>
28 #include <linux/rtc.h>
29 #include <linux/bcd.h>
30 #include <linux/watchdog.h>
31 @@ -95,6 +96,15 @@ enum close_state {
32 CLOSE_STATE_ALLOW = 0x2342,
35 +enum pcf50606_suspend_states {
36 + PCF50606_SS_RUNNING,
37 + PCF50606_SS_STARTING_SUSPEND,
38 + PCF50606_SS_COMPLETED_SUSPEND,
39 + PCF50606_SS_RESUMING_BUT_NOT_US_YET,
40 + PCF50606_SS_STARTING_RESUME,
41 + PCF50606_SS_COMPLETED_RESUME,
44 struct pcf50606_data {
45 struct i2c_client client;
46 struct pcf50606_platform_data *pdata;
47 @@ -110,6 +120,7 @@ struct pcf50606_data {
51 + enum pcf50606_suspend_states suspend_state;
54 u_int8_t dcdc1, dcdc2;
55 @@ -160,6 +171,10 @@ static const u_int16_t ntc_table_10k_3370B[] = {
56 static inline int __reg_write(struct pcf50606_data *pcf, u_int8_t reg,
59 + if (pcf->suspend_state == PCF50606_SS_COMPLETED_SUSPEND) {
60 + dev_err(&pcf->client.dev, "__reg_write while suspended.\n");
63 return i2c_smbus_write_byte_data(&pcf->client, reg, val);
66 @@ -178,6 +193,10 @@ static inline int32_t __reg_read(struct pcf50606_data *pcf, u_int8_t reg)
70 + if (pcf->suspend_state == PCF50606_SS_COMPLETED_SUSPEND) {
71 + dev_err(&pcf->client.dev, "__reg_read while suspended.\n");
74 ret = i2c_smbus_read_byte_data(&pcf->client, reg);
77 @@ -569,6 +588,48 @@ static void pcf50606_work(struct work_struct *work)
79 mutex_lock(&pcf->working_lock);
83 + if (!&pcf->client.dev)
87 + * if we are presently suspending, we are not in a position to deal
88 + * with pcf50606 interrupts at all.
90 + * Because we didn't clear the int pending registers, there will be
91 + * no edge / interrupt waiting for us when we wake. But it is OK
92 + * because at the end of our resume, we call this workqueue function
93 + * gratuitously, clearing the pending register and re-enabling
94 + * servicing this interrupt.
97 + if ((pcf->suspend_state == PCF50606_SS_STARTING_SUSPEND) ||
98 + (pcf->suspend_state == PCF50606_SS_COMPLETED_SUSPEND))
102 + * If we are inside suspend -> resume completion time we don't attempt
103 + * service until we have fully resumed. Although we could talk to the
104 + * device as soon as I2C is up, the regs in the device which we might
105 + * choose to modify as part of the service action have not been
106 + * reloaded with their pre-suspend states yet. Therefore we will
107 + * defer our service if we are called like that until our resume has
110 + * This shouldn't happen any more because we disable servicing this
111 + * interrupt in suspend and don't re-enable it until resume is
115 + if (pcf->suspend_state &&
116 + (pcf->suspend_state != PCF50606_SS_COMPLETED_RESUME))
119 + /* this is the case early in resume! Sanity check! */
120 + if (i2c_get_clientdata(&pcf->client) == NULL)
124 * p35 pcf50606 datasheet rev 2.2:
125 * ''The system controller shall read all interrupt registers in
126 @@ -576,10 +637,27 @@ static void pcf50606_work(struct work_struct *work)
127 * because if you don't INT# gets stuck asserted forever after a
130 - ret = i2c_smbus_read_i2c_block_data(&pcf->client, PCF50606_REG_INT1, 3,
133 + ret = i2c_smbus_read_i2c_block_data(&pcf->client, PCF50606_REG_INT1,
134 + sizeof(pcfirq), pcfirq);
135 + if (ret != sizeof(pcfirq)) {
136 DEBUGPC("Oh crap PMU IRQ register read failed %d\n", ret);
138 + * it shouldn't fail, we no longer attempt to use
139 + * I2C while it can be suspended. But we don't have
140 + * much option but to retry if if it ever did fail,
141 + * because if we don't service the interrupt to clear
142 + * it, we will never see another PMU interrupt edge.
147 + /* hey did we just resume? (because we don't get here unless we are
148 + * running normally or the first call after resumption)
150 + * pcf50606 resume is really really over now then.
152 + if (pcf->suspend_state != PCF50606_SS_RUNNING)
153 + pcf->suspend_state = PCF50606_SS_RUNNING;
155 if (!pcf->coldplug_done) {
156 DEBUGPC("PMU Coldplug init\n");
157 @@ -814,10 +892,26 @@ static void pcf50606_work(struct work_struct *work)
163 input_sync(pcf->input_dev);
164 put_device(&pcf->client.dev);
165 mutex_unlock(&pcf->working_lock);
171 + if ((pcf->suspend_state != PCF50606_SS_STARTING_SUSPEND) &&
172 + (pcf->suspend_state != PCF50606_SS_COMPLETED_SUSPEND)) {
174 + dev_info(&pcf->client.dev, "rescheduling interrupt service\n");
176 + if (!schedule_work(&pcf->work))
177 + dev_err(&pcf->client.dev, "int service reschedule failed\n");
179 + /* we don't put the device here, hold it for next time */
180 + mutex_unlock(&pcf->working_lock);
183 static irqreturn_t pcf50606_irq(int irq, void *_pcf)
184 @@ -828,7 +922,7 @@ static irqreturn_t pcf50606_irq(int irq, void *_pcf)
186 get_device(&pcf->client.dev);
187 if (!schedule_work(&pcf->work) && !pcf->working)
188 - dev_dbg(&pcf->client.dev, "work item may be lost\n");
189 + dev_err(&pcf->client.dev, "pcf irq work already queued.\n");
193 @@ -1882,12 +1976,27 @@ static int pcf50606_suspend(struct device *dev, pm_message_t state)
194 struct pcf50606_data *pcf = i2c_get_clientdata(client);
197 + /* we suspend once (!) as late as possible in the suspend sequencing */
199 + if ((state.event != PM_EVENT_SUSPEND) ||
200 + (pcf->suspend_state != PCF50606_SS_RUNNING))
203 /* The general idea is to power down all unused power supplies,
204 * and then mask all PCF50606 interrup sources but EXTONR, ONKEYF
207 mutex_lock(&pcf->lock);
209 + pcf->suspend_state = PCF50606_SS_STARTING_SUSPEND;
211 + /* we are not going to service any further interrupts until we
212 + * resume. If the IRQ workqueue is still pending in the background,
213 + * it will bail when it sees we set suspend state above.
216 + disable_irq(pcf->irq);
218 /* Save all registers that don't "survive" standby state */
219 pcf->standby_regs.dcdc1 = __reg_read(pcf, PCF50606_REG_DCDC1);
220 pcf->standby_regs.dcdc2 = __reg_read(pcf, PCF50606_REG_DCDC2);
221 @@ -1928,6 +2037,8 @@ static int pcf50606_suspend(struct device *dev, pm_message_t state)
222 __reg_write(pcf, PCF50606_REG_INT2M, ~INT2M_RESUMERS & 0xff);
223 __reg_write(pcf, PCF50606_REG_INT3M, ~INT3M_RESUMERS & 0xff);
225 + pcf->suspend_state = PCF50606_SS_COMPLETED_SUSPEND;
227 mutex_unlock(&pcf->lock);
230 @@ -1940,6 +2051,8 @@ static int pcf50606_resume(struct device *dev)
232 mutex_lock(&pcf->lock);
234 + pcf->suspend_state = PCF50606_SS_STARTING_RESUME;
236 /* Resume all saved registers that don't "survive" standby state */
237 __reg_write(pcf, PCF50606_REG_INT1M, pcf->standby_regs.int1m);
238 __reg_write(pcf, PCF50606_REG_INT2M, pcf->standby_regs.int2m);
239 @@ -1958,10 +2071,17 @@ static int pcf50606_resume(struct device *dev)
240 __reg_write(pcf, PCF50606_REG_ADCC2, pcf->standby_regs.adcc2);
241 __reg_write(pcf, PCF50606_REG_PWMC1, pcf->standby_regs.pwmc1);
243 + pcf->suspend_state = PCF50606_SS_COMPLETED_RESUME;
245 + enable_irq(pcf->irq);
247 mutex_unlock(&pcf->lock);
249 - /* Hack to fix the gta01 power button problem on resume */
250 - pcf50606_irq(0, pcf);
251 + /* Call PCF work function; this fixes an issue on the gta01 where
252 + * the power button "goes away" if it is used to wake the device.
254 + get_device(&pcf->client.dev);
255 + pcf50606_work(&pcf->work);
259 @@ -1999,9 +2119,25 @@ static int pcf50606_plat_remove(struct platform_device *pdev)
263 +/* We have this purely to capture an early indication that we are coming out
264 + * of suspend, before our device resume got called; async interrupt service is
265 + * interested in this.
268 +static int pcf50606_plat_resume(struct platform_device *pdev)
270 + /* i2c_get_clientdata(to_i2c_client(&pdev->dev)) returns NULL at this
271 + * early resume time so we have to use pcf50606_global
273 + pcf50606_global->suspend_state = PCF50606_SS_RESUMING_BUT_NOT_US_YET;
278 static struct platform_driver pcf50606_plat_driver = {
279 .probe = pcf50606_plat_probe,
280 .remove = pcf50606_plat_remove,
281 + .resume_early = pcf50606_plat_resume,
283 .owner = THIS_MODULE,