add support for target 3c24xx (more known as Openmoko GTA02 "Freerunner") and merge...
[openwrt.git] / target / linux / s3c24xx / patches / 0176-fix-pcf50633-suspend-state-as-enum.patch.patch
1 From 591a5d7e8da059de812d315b865fd5c0fa89071e Mon Sep 17 00:00:00 2001
2 From: Andy Green <andy@openmoko.com>
3 Date: Fri, 25 Jul 2008 23:06:15 +0100
4 Subject: [PATCH] fix-pcf50633-suspend-state-as-enum.patch
5
6 Use an enum to define pcf50633 suspend / resume state.
7 Add PCF50633_SS_RESUMING_BUT_NOT_US_YET to be the state
8 early in resume: add platform driver resume function just
9 to set this state so we can differentiate between early
10 resume and late suspend.
11
12 Signed-off-by: Andy Green <andy@openmoko.com>
13 ---
14  drivers/i2c/chips/pcf50633.c |   73 ++++++++++++++++++++++++++++++++++--------
15  1 files changed, 59 insertions(+), 14 deletions(-)
16
17 diff --git a/drivers/i2c/chips/pcf50633.c b/drivers/i2c/chips/pcf50633.c
18 index 37dfca6..87a3003 100644
19 --- a/drivers/i2c/chips/pcf50633.c
20 +++ b/drivers/i2c/chips/pcf50633.c
21 @@ -108,6 +108,16 @@ enum charger_type {
22  
23  #define MAX_ADC_FIFO_DEPTH 8
24  
25 +enum pcf50633_suspend_states {
26 +       PCF50633_SS_RUNNING,
27 +       PCF50633_SS_STARTING_SUSPEND,
28 +       PCF50633_SS_COMPLETED_SUSPEND,
29 +       PCF50633_SS_RESUMING_BUT_NOT_US_YET,
30 +       PCF50633_SS_STARTING_RESUME,
31 +       PCF50633_SS_COMPLETED_RESUME,
32 +};
33 +
34 +
35  struct pcf50633_data {
36         struct i2c_client client;
37         struct pcf50633_platform_data *pdata;
38 @@ -122,9 +132,9 @@ struct pcf50633_data {
39         int allow_close;
40         int onkey_seconds;
41         int irq;
42 -       int have_been_suspended;
43 +       enum pcf50633_suspend_states suspend_state;
44         int usb_removal_count;
45 -       unsigned char pcfirq_resume[5];
46 +       u8 pcfirq_resume[5];
47         int probe_completed;
48  
49         /* if he pulls battery while charging, we notice that and correctly
50 @@ -191,7 +201,7 @@ static struct platform_device *pcf50633_pdev;
51  
52  static int __reg_write(struct pcf50633_data *pcf, u_int8_t reg, u_int8_t val)
53  {
54 -       if (pcf->have_been_suspended == 1) {
55 +       if (pcf->suspend_state == PCF50633_SS_COMPLETED_SUSPEND) {
56                 dev_err(&pcf->client.dev, "__reg_write while suspended\n");
57                 dump_stack();
58         }
59 @@ -213,7 +223,7 @@ static int32_t __reg_read(struct pcf50633_data *pcf, u_int8_t reg)
60  {
61         int32_t ret;
62  
63 -       if (pcf->have_been_suspended == 1) {
64 +       if (pcf->suspend_state == PCF50633_SS_COMPLETED_SUSPEND) {
65                 dev_err(&pcf->client.dev, "__reg_read while suspended\n");
66                 dump_stack();
67         }
68 @@ -630,7 +640,8 @@ static void pcf50633_work_usbcurlim(struct work_struct *work)
69         /* we got a notification from USB stack before we completed resume...
70          * that can only make trouble, reschedule for a retry
71          */
72 -       if (pcf->have_been_suspended && (pcf->have_been_suspended < 3))
73 +       if (pcf->suspend_state &&
74 +                    (pcf->suspend_state < PCF50633_SS_COMPLETED_RESUME))
75                 goto reschedule;
76  
77         /*
78 @@ -751,6 +762,21 @@ static void pcf50633_work(struct work_struct *work)
79         pcf->working = 1;
80  
81         /*
82 +        * if we are presently suspending, we are not in a position to deal
83 +        * with pcf50633 interrupts at all.
84 +        *
85 +        * Because we didn't clear the int pending registers, there will be
86 +        * no edge / interrupt waiting for us when we wake.  But it is OK
87 +        * because at the end of our resume, we call this workqueue function
88 +        * gratuitously, clearing the pending register and re-enabling
89 +        * servicing this interrupt.
90 +        */
91 +
92 +       if ((pcf->suspend_state == PCF50633_SS_STARTING_SUSPEND) ||
93 +           (pcf->suspend_state == PCF50633_SS_COMPLETED_SUSPEND))
94 +               goto bail;
95 +
96 +       /*
97          * If we are inside suspend -> resume completion time we don't attempt
98          * service until we have fully resumed.  Although we could talk to the
99          * device as soon as I2C is up, the regs in the device which we might
100 @@ -1154,8 +1180,9 @@ reschedule:
101         /* EXCEPTION: if we are in the middle of suspending, we don't have
102          * time to hang around since we may be turned off core 1V3 already
103          */
104 -       if (pcf->have_been_suspended != 1) {
105 -               msleep(50);
106 +       if ((pcf->suspend_state != PCF50633_SS_STARTING_SUSPEND) &&
107 +           (pcf->suspend_state != PCF50633_SS_COMPLETED_SUSPEND)) {
108 +               msleep(10);
109                 dev_info(&pcf->client.dev, "rescheduling interrupt service\n");
110         }
111         if (!schedule_work(&pcf->work))
112 @@ -2315,8 +2342,9 @@ static int pcf50633_suspend(struct device *dev, pm_message_t state)
113  
114         /* we suspend once (!) as late as possible in the suspend sequencing */
115  
116 -       if ((state.event != PM_EVENT_SUSPEND) || (pcf->have_been_suspended))
117 -               return 0;
118 +       if ((state.event != PM_EVENT_SUSPEND) ||
119 +           (pcf->suspend_state != PCF50633_SS_RUNNING))
120 +               return -EBUSY;
121  
122         /* The general idea is to power down all unused power supplies,
123          * and then mask all PCF50633 interrupt sources but EXTONR, ONKEYF
124 @@ -2380,7 +2408,7 @@ static int pcf50633_suspend(struct device *dev, pm_message_t state)
125         if (ret)
126                 dev_err(dev, "Failed to set wake masks :-( %d\n", ret);
127  
128 -       pcf->have_been_suspended = 1;
129 +       pcf->suspend_state = PCF50633_SS_COMPLETED_SUSPEND;
130  
131         mutex_unlock(&pcf->lock);
132  
133 @@ -2393,7 +2421,8 @@ int pcf50633_ready(struct pcf50633_data *pcf)
134         if (!pcf)
135                 return -EACCES;
136  
137 -       if (pcf->have_been_suspended && (pcf->have_been_suspended < 3))
138 +       if ((pcf->suspend_state != PCF50633_SS_RUNNING) &&
139 +           (pcf->suspend_state < PCF50633_SS_COMPLETED_RESUME))
140                 return -EBUSY;
141  
142         return 0;
143 @@ -2428,10 +2457,10 @@ static int pcf50633_resume(struct device *dev)
144         u8 res[5];
145  
146         dev_info(dev, "pcf50633_resume suspended on entry = %d\n",
147 -                                                     pcf->have_been_suspended);
148 +                                                (int)pcf->suspend_state);
149         mutex_lock(&pcf->lock);
150  
151 -       pcf->have_been_suspended = 2; /* resuming */
152 +       pcf->suspend_state = PCF50633_SS_STARTING_RESUME;
153  
154         /* these guys get reset while pcf50633 is suspend state, refresh */
155  
156 @@ -2464,7 +2493,7 @@ static int pcf50633_resume(struct device *dev)
157         if (ret)
158                 dev_err(dev, "Failed to set int masks :-( %d\n", ret);
159  
160 -       pcf->have_been_suspended = 3; /* resume completed */
161 +       pcf->suspend_state = PCF50633_SS_COMPLETED_RESUME;
162  
163         mutex_unlock(&pcf->lock);
164  
165 @@ -2498,6 +2527,21 @@ static struct i2c_driver pcf50633_driver = {
166         .detach_client  = pcf50633_detach_client,
167  };
168  
169 +/* we have this purely to capture an early indication that we are coming out
170 + * of suspend, before our device resume got called; async interrupt service is
171 + * interested in this
172 + */
173 +
174 +static int pcf50633_plat_resume(struct platform_device *pdev)
175 +{
176 +       /* i2c_get_clientdata(to_i2c_client(&pdev->dev)) returns NULL at this
177 +        * early resume time so we have to use pcf50633_global
178 +        */
179 +       pcf50633_global->suspend_state = PCF50633_SS_RESUMING_BUT_NOT_US_YET;
180 +
181 +       return 0;
182 +}
183 +
184  /* platform driver, since i2c devices don't have platform_data */
185  static int __init pcf50633_plat_probe(struct platform_device *pdev)
186  {
187 @@ -2519,6 +2563,7 @@ static int pcf50633_plat_remove(struct platform_device *pdev)
188  static struct platform_driver pcf50633_plat_driver = {
189         .probe  = pcf50633_plat_probe,
190         .remove = pcf50633_plat_remove,
191 +       .resume_early = pcf50633_plat_resume,
192         .driver = {
193                 .owner  = THIS_MODULE,
194                 .name   = "pcf50633",
195 -- 
196 1.5.6.3
197