fe16b482e912c395f6553e23cdb6d4d1bb54e780
[linux-flexiantxendom0-natty.git] / drivers / power / s3c_adc_battery.c
1 /*
2  *      iPAQ h1930/h1940/rx1950 battery controler driver
3  *      Copyright (c) Vasily Khoruzhick
4  *      Based on h1940_battery.c by Arnaud Patard
5  *
6  * This file is subject to the terms and conditions of the GNU General Public
7  * License.  See the file COPYING in the main directory of this archive for
8  * more details.
9  *
10  */
11
12 #include <linux/interrupt.h>
13 #include <linux/platform_device.h>
14 #include <linux/power_supply.h>
15 #include <linux/leds.h>
16 #include <linux/gpio.h>
17 #include <linux/err.h>
18 #include <linux/timer.h>
19 #include <linux/jiffies.h>
20 #include <linux/s3c_adc_battery.h>
21 #include <linux/errno.h>
22 #include <linux/init.h>
23
24 #include <plat/adc.h>
25
26 #define BAT_POLL_INTERVAL               10000 /* ms */
27 #define JITTER_DELAY                    500 /* ms */
28
29 struct s3c_adc_bat {
30         struct power_supply             psy;
31         struct s3c_adc_client           *client;
32         struct s3c_adc_bat_pdata        *pdata;
33         int                             volt_value;
34         int                             cur_value;
35         unsigned int                    timestamp;
36         int                             level;
37         int                             status;
38         int                             cable_plugged:1;
39 };
40
41 static struct delayed_work bat_work;
42
43 static void s3c_adc_bat_ext_power_changed(struct power_supply *psy)
44 {
45         schedule_delayed_work(&bat_work,
46                 msecs_to_jiffies(JITTER_DELAY));
47 }
48
49 static enum power_supply_property s3c_adc_backup_bat_props[] = {
50         POWER_SUPPLY_PROP_VOLTAGE_NOW,
51         POWER_SUPPLY_PROP_VOLTAGE_MIN,
52         POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
53 };
54
55 static int s3c_adc_backup_bat_get_property(struct power_supply *psy,
56                                 enum power_supply_property psp,
57                                 union power_supply_propval *val)
58 {
59         struct s3c_adc_bat *bat = container_of(psy, struct s3c_adc_bat, psy);
60
61         if (!bat) {
62                 dev_err(psy->dev, "%s: no battery infos ?!\n", __func__);
63                 return -EINVAL;
64         }
65
66         if (bat->volt_value < 0 ||
67                 jiffies_to_msecs(jiffies - bat->timestamp) >
68                         BAT_POLL_INTERVAL) {
69                 bat->volt_value = s3c_adc_read(bat->client,
70                         bat->pdata->backup_volt_channel);
71                 bat->volt_value *= bat->pdata->backup_volt_mult;
72                 bat->timestamp = jiffies;
73         }
74
75         switch (psp) {
76         case POWER_SUPPLY_PROP_VOLTAGE_NOW:
77                 val->intval = bat->volt_value;
78                 return 0;
79         case POWER_SUPPLY_PROP_VOLTAGE_MIN:
80                 val->intval = bat->pdata->backup_volt_min;
81                 return 0;
82         case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
83                 val->intval = bat->pdata->backup_volt_max;
84                 return 0;
85         default:
86                 return -EINVAL;
87         }
88 }
89
90 static struct s3c_adc_bat backup_bat = {
91         .psy = {
92                 .name           = "backup-battery",
93                 .type           = POWER_SUPPLY_TYPE_BATTERY,
94                 .properties     = s3c_adc_backup_bat_props,
95                 .num_properties = ARRAY_SIZE(s3c_adc_backup_bat_props),
96                 .get_property   = s3c_adc_backup_bat_get_property,
97                 .use_for_apm    = 1,
98         },
99 };
100
101 static enum power_supply_property s3c_adc_main_bat_props[] = {
102         POWER_SUPPLY_PROP_STATUS,
103         POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
104         POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN,
105         POWER_SUPPLY_PROP_CHARGE_NOW,
106         POWER_SUPPLY_PROP_VOLTAGE_NOW,
107         POWER_SUPPLY_PROP_CURRENT_NOW,
108 };
109
110 static int calc_full_volt(int volt_val, int cur_val, int impedance)
111 {
112         return volt_val + cur_val * impedance / 1000;
113 }
114
115 static int s3c_adc_bat_get_property(struct power_supply *psy,
116                                     enum power_supply_property psp,
117                                     union power_supply_propval *val)
118 {
119         struct s3c_adc_bat *bat = container_of(psy, struct s3c_adc_bat, psy);
120
121         int new_level;
122         int full_volt;
123         const struct s3c_adc_bat_thresh *lut = bat->pdata->lut_noac;
124         unsigned int lut_size = bat->pdata->lut_noac_cnt;
125
126         if (!bat) {
127                 dev_err(psy->dev, "no battery infos ?!\n");
128                 return -EINVAL;
129         }
130
131         if (bat->volt_value < 0 || bat->cur_value < 0 ||
132                 jiffies_to_msecs(jiffies - bat->timestamp) >
133                         BAT_POLL_INTERVAL) {
134                 bat->volt_value = s3c_adc_read(bat->client,
135                         bat->pdata->volt_channel) * bat->pdata->volt_mult;
136                 bat->cur_value = s3c_adc_read(bat->client,
137                         bat->pdata->current_channel) * bat->pdata->current_mult;
138                 bat->timestamp = jiffies;
139         }
140
141         if (bat->cable_plugged &&
142                 ((bat->pdata->gpio_charge_finished < 0) ||
143                 !gpio_get_value(bat->pdata->gpio_charge_finished))) {
144                 lut = bat->pdata->lut_acin;
145                 lut_size = bat->pdata->lut_acin_cnt;
146         }
147
148         new_level = 100000;
149         full_volt = calc_full_volt((bat->volt_value / 1000),
150                 (bat->cur_value / 1000), bat->pdata->internal_impedance);
151
152         if (full_volt < calc_full_volt(lut->volt, lut->cur,
153                 bat->pdata->internal_impedance)) {
154                 lut_size--;
155                 while (lut_size--) {
156                         int lut_volt1;
157                         int lut_volt2;
158
159                         lut_volt1 = calc_full_volt(lut[0].volt, lut[0].cur,
160                                 bat->pdata->internal_impedance);
161                         lut_volt2 = calc_full_volt(lut[1].volt, lut[1].cur,
162                                 bat->pdata->internal_impedance);
163                         if (full_volt < lut_volt1 && full_volt >= lut_volt2) {
164                                 new_level = (lut[1].level +
165                                         (lut[0].level - lut[1].level) *
166                                         (full_volt - lut_volt2) /
167                                         (lut_volt1 - lut_volt2)) * 1000;
168                                 break;
169                         }
170                         new_level = lut[1].level * 1000;
171                         lut++;
172                 }
173         }
174
175         bat->level = new_level;
176
177         switch (psp) {
178         case POWER_SUPPLY_PROP_STATUS:
179                 if (bat->pdata->gpio_charge_finished < 0)
180                         val->intval = bat->level == 100000 ?
181                                 POWER_SUPPLY_STATUS_FULL : bat->status;
182                 else
183                         val->intval = bat->status;
184                 return 0;
185         case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
186                 val->intval = 100000;
187                 return 0;
188         case POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN:
189                 val->intval = 0;
190                 return 0;
191         case POWER_SUPPLY_PROP_CHARGE_NOW:
192                 val->intval = bat->level;
193                 return 0;
194         case POWER_SUPPLY_PROP_VOLTAGE_NOW:
195                 val->intval = bat->volt_value;
196                 return 0;
197         case POWER_SUPPLY_PROP_CURRENT_NOW:
198                 val->intval = bat->cur_value;
199                 return 0;
200         default:
201                 return -EINVAL;
202         }
203 }
204
205 static struct s3c_adc_bat main_bat = {
206         .psy = {
207                 .name                   = "main-battery",
208                 .type                   = POWER_SUPPLY_TYPE_BATTERY,
209                 .properties             = s3c_adc_main_bat_props,
210                 .num_properties         = ARRAY_SIZE(s3c_adc_main_bat_props),
211                 .get_property           = s3c_adc_bat_get_property,
212                 .external_power_changed = s3c_adc_bat_ext_power_changed,
213                 .use_for_apm            = 1,
214         },
215 };
216
217 static void s3c_adc_bat_work(struct work_struct *work)
218 {
219         struct s3c_adc_bat *bat = &main_bat;
220         int is_charged;
221         int is_plugged;
222         static int was_plugged;
223
224         is_plugged = power_supply_am_i_supplied(&bat->psy);
225         bat->cable_plugged = is_plugged;
226         if (is_plugged != was_plugged) {
227                 was_plugged = is_plugged;
228                 if (is_plugged) {
229                         if (bat->pdata->enable_charger)
230                                 bat->pdata->enable_charger();
231                         bat->status = POWER_SUPPLY_STATUS_CHARGING;
232                 } else {
233                         if (bat->pdata->disable_charger)
234                                 bat->pdata->disable_charger();
235                         bat->status = POWER_SUPPLY_STATUS_DISCHARGING;
236                 }
237         } else {
238                 if ((bat->pdata->gpio_charge_finished >= 0) && is_plugged) {
239                         is_charged = gpio_get_value(
240                                 main_bat.pdata->gpio_charge_finished);
241                         if (is_charged) {
242                                 if (bat->pdata->disable_charger)
243                                         bat->pdata->disable_charger();
244                                 bat->status = POWER_SUPPLY_STATUS_FULL;
245                         } else {
246                                 if (bat->pdata->enable_charger)
247                                         bat->pdata->enable_charger();
248                                 bat->status = POWER_SUPPLY_STATUS_CHARGING;
249                         }
250                 }
251         }
252
253         power_supply_changed(&bat->psy);
254 }
255
256 static irqreturn_t s3c_adc_bat_charged(int irq, void *dev_id)
257 {
258         schedule_delayed_work(&bat_work,
259                 msecs_to_jiffies(JITTER_DELAY));
260         return IRQ_HANDLED;
261 }
262
263 static int __init s3c_adc_bat_probe(struct platform_device *pdev)
264 {
265         struct s3c_adc_client   *client;
266         struct s3c_adc_bat_pdata *pdata = pdev->dev.platform_data;
267         int ret;
268
269         client = s3c_adc_register(pdev, NULL, NULL, 0);
270         if (IS_ERR(client)) {
271                 dev_err(&pdev->dev, "cannot register adc\n");
272                 return PTR_ERR(client);
273         }
274
275         platform_set_drvdata(pdev, client);
276
277         main_bat.client = client;
278         main_bat.pdata = pdata;
279         main_bat.volt_value = -1;
280         main_bat.cur_value = -1;
281         main_bat.cable_plugged = 0;
282         main_bat.status = POWER_SUPPLY_STATUS_DISCHARGING;
283
284         ret = power_supply_register(&pdev->dev, &main_bat.psy);
285         if (ret)
286                 goto err_reg_main;
287         if (pdata->backup_volt_mult) {
288                 backup_bat.client = client;
289                 backup_bat.pdata = pdev->dev.platform_data;
290                 backup_bat.volt_value = -1;
291                 ret = power_supply_register(&pdev->dev, &backup_bat.psy);
292                 if (ret)
293                         goto err_reg_backup;
294         }
295
296         INIT_DELAYED_WORK(&bat_work, s3c_adc_bat_work);
297
298         if (pdata->gpio_charge_finished >= 0) {
299                 ret = gpio_request(pdata->gpio_charge_finished, "charged");
300                 if (ret)
301                         goto err_gpio;
302
303                 ret = request_irq(gpio_to_irq(pdata->gpio_charge_finished),
304                                 s3c_adc_bat_charged,
305                                 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
306                                 "battery charged", NULL);
307                 if (ret)
308                         goto err_irq;
309         }
310
311         if (pdata->init) {
312                 ret = pdata->init();
313                 if (ret)
314                         goto err_platform;
315         }
316
317         dev_info(&pdev->dev, "successfully loaded\n");
318         device_init_wakeup(&pdev->dev, 1);
319
320         /* Schedule timer to check current status */
321         schedule_delayed_work(&bat_work,
322                 msecs_to_jiffies(JITTER_DELAY));
323
324         return 0;
325
326 err_platform:
327         if (pdata->gpio_charge_finished >= 0)
328                 free_irq(gpio_to_irq(pdata->gpio_charge_finished), NULL);
329 err_irq:
330         if (pdata->gpio_charge_finished >= 0)
331                 gpio_free(pdata->gpio_charge_finished);
332 err_gpio:
333         if (pdata->backup_volt_mult)
334                 power_supply_unregister(&backup_bat.psy);
335 err_reg_backup:
336         power_supply_unregister(&main_bat.psy);
337 err_reg_main:
338         return ret;
339 }
340
341 static int s3c_adc_bat_remove(struct platform_device *pdev)
342 {
343         struct s3c_adc_client *client = platform_get_drvdata(pdev);
344         struct s3c_adc_bat_pdata *pdata = pdev->dev.platform_data;
345
346         power_supply_unregister(&main_bat.psy);
347         if (pdata->backup_volt_mult)
348                 power_supply_unregister(&backup_bat.psy);
349
350         s3c_adc_release(client);
351
352         if (pdata->gpio_charge_finished >= 0) {
353                 free_irq(gpio_to_irq(pdata->gpio_charge_finished), NULL);
354                 gpio_free(pdata->gpio_charge_finished);
355         }
356
357         cancel_delayed_work(&bat_work);
358
359         if (pdata->exit)
360                 pdata->exit();
361
362         return 0;
363 }
364
365 #ifdef CONFIG_PM
366 static int s3c_adc_bat_suspend(struct platform_device *pdev,
367         pm_message_t state)
368 {
369         struct s3c_adc_bat_pdata *pdata = pdev->dev.platform_data;
370
371         if (pdata->gpio_charge_finished >= 0) {
372                 if (device_may_wakeup(&pdev->dev))
373                         enable_irq_wake(
374                                 gpio_to_irq(pdata->gpio_charge_finished));
375                 else {
376                         disable_irq(gpio_to_irq(pdata->gpio_charge_finished));
377                         main_bat.pdata->disable_charger();
378                 }
379         }
380
381         return 0;
382 }
383
384 static int s3c_adc_bat_resume(struct platform_device *pdev)
385 {
386         struct s3c_adc_bat_pdata *pdata = pdev->dev.platform_data;
387
388         if (pdata->gpio_charge_finished >= 0) {
389                 if (device_may_wakeup(&pdev->dev))
390                         disable_irq_wake(
391                                 gpio_to_irq(pdata->gpio_charge_finished));
392                 else
393                         enable_irq(gpio_to_irq(pdata->gpio_charge_finished));
394         }
395
396         /* Schedule timer to check current status */
397         schedule_delayed_work(&bat_work,
398                 msecs_to_jiffies(JITTER_DELAY));
399
400         return 0;
401 }
402 #else
403 #define s3c_adc_battery_suspend NULL
404 #define s3c_adc_battery_resume NULL
405 #endif
406
407 static struct platform_driver s3c_adc_bat_driver = {
408         .driver         = {
409                 .name   = "s3c-adc-battery",
410         },
411         .probe          = s3c_adc_bat_probe,
412         .remove         = s3c_adc_bat_remove,
413         .suspend        = s3c_adc_bat_suspend,
414         .resume         = s3c_adc_bat_resume,
415 };
416
417 static int __init s3c_adc_bat_init(void)
418 {
419         return platform_driver_register(&s3c_adc_bat_driver);
420 }
421 module_init(s3c_adc_bat_init);
422
423 static void __exit s3c_adc_bat_exit(void)
424 {
425         platform_driver_unregister(&s3c_adc_bat_driver);
426 }
427 module_exit(s3c_adc_bat_exit);
428
429 MODULE_AUTHOR("Vasily Khoruzhick <anarsoul@gmail.com>");
430 MODULE_DESCRIPTION("iPAQ H1930/H1940/RX1950 battery controler driver");
431 MODULE_LICENSE("GPL");