GUI: Fix Tomato RAF theme for all builds. Compilation typo.
[tomato.git] / release / src-rt-6.x.4708 / linux / linux-2.6.36 / drivers / power / s3c_adc_battery.c
blobfe16b482e912c395f6553e23cdb6d4d1bb54e780
1 /*
2 * iPAQ h1930/h1940/rx1950 battery controler driver
3 * Copyright (c) Vasily Khoruzhick
4 * Based on h1940_battery.c by Arnaud Patard
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.
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>
24 #include <plat/adc.h>
26 #define BAT_POLL_INTERVAL 10000 /* ms */
27 #define JITTER_DELAY 500 /* ms */
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;
41 static struct delayed_work bat_work;
43 static void s3c_adc_bat_ext_power_changed(struct power_supply *psy)
45 schedule_delayed_work(&bat_work,
46 msecs_to_jiffies(JITTER_DELAY));
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,
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)
59 struct s3c_adc_bat *bat = container_of(psy, struct s3c_adc_bat, psy);
61 if (!bat) {
62 dev_err(psy->dev, "%s: no battery infos ?!\n", __func__);
63 return -EINVAL;
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;
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;
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,
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,
110 static int calc_full_volt(int volt_val, int cur_val, int impedance)
112 return volt_val + cur_val * impedance / 1000;
115 static int s3c_adc_bat_get_property(struct power_supply *psy,
116 enum power_supply_property psp,
117 union power_supply_propval *val)
119 struct s3c_adc_bat *bat = container_of(psy, struct s3c_adc_bat, psy);
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;
126 if (!bat) {
127 dev_err(psy->dev, "no battery infos ?!\n");
128 return -EINVAL;
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;
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;
148 new_level = 100000;
149 full_volt = calc_full_volt((bat->volt_value / 1000),
150 (bat->cur_value / 1000), bat->pdata->internal_impedance);
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;
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;
170 new_level = lut[1].level * 1000;
171 lut++;
175 bat->level = new_level;
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;
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,
217 static void s3c_adc_bat_work(struct work_struct *work)
219 struct s3c_adc_bat *bat = &main_bat;
220 int is_charged;
221 int is_plugged;
222 static int was_plugged;
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;
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;
253 power_supply_changed(&bat->psy);
256 static irqreturn_t s3c_adc_bat_charged(int irq, void *dev_id)
258 schedule_delayed_work(&bat_work,
259 msecs_to_jiffies(JITTER_DELAY));
260 return IRQ_HANDLED;
263 static int __init s3c_adc_bat_probe(struct platform_device *pdev)
265 struct s3c_adc_client *client;
266 struct s3c_adc_bat_pdata *pdata = pdev->dev.platform_data;
267 int ret;
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);
275 platform_set_drvdata(pdev, client);
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;
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;
296 INIT_DELAYED_WORK(&bat_work, s3c_adc_bat_work);
298 if (pdata->gpio_charge_finished >= 0) {
299 ret = gpio_request(pdata->gpio_charge_finished, "charged");
300 if (ret)
301 goto err_gpio;
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;
311 if (pdata->init) {
312 ret = pdata->init();
313 if (ret)
314 goto err_platform;
317 dev_info(&pdev->dev, "successfully loaded\n");
318 device_init_wakeup(&pdev->dev, 1);
320 /* Schedule timer to check current status */
321 schedule_delayed_work(&bat_work,
322 msecs_to_jiffies(JITTER_DELAY));
324 return 0;
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;
341 static int s3c_adc_bat_remove(struct platform_device *pdev)
343 struct s3c_adc_client *client = platform_get_drvdata(pdev);
344 struct s3c_adc_bat_pdata *pdata = pdev->dev.platform_data;
346 power_supply_unregister(&main_bat.psy);
347 if (pdata->backup_volt_mult)
348 power_supply_unregister(&backup_bat.psy);
350 s3c_adc_release(client);
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);
357 cancel_delayed_work(&bat_work);
359 if (pdata->exit)
360 pdata->exit();
362 return 0;
365 #ifdef CONFIG_PM
366 static int s3c_adc_bat_suspend(struct platform_device *pdev,
367 pm_message_t state)
369 struct s3c_adc_bat_pdata *pdata = pdev->dev.platform_data;
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();
381 return 0;
384 static int s3c_adc_bat_resume(struct platform_device *pdev)
386 struct s3c_adc_bat_pdata *pdata = pdev->dev.platform_data;
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));
396 /* Schedule timer to check current status */
397 schedule_delayed_work(&bat_work,
398 msecs_to_jiffies(JITTER_DELAY));
400 return 0;
402 #else
403 #define s3c_adc_battery_suspend NULL
404 #define s3c_adc_battery_resume NULL
405 #endif
407 static struct platform_driver s3c_adc_bat_driver = {
408 .driver = {
409 .name = "s3c-adc-battery",
411 .probe = s3c_adc_bat_probe,
412 .remove = s3c_adc_bat_remove,
413 .suspend = s3c_adc_bat_suspend,
414 .resume = s3c_adc_bat_resume,
417 static int __init s3c_adc_bat_init(void)
419 return platform_driver_register(&s3c_adc_bat_driver);
421 module_init(s3c_adc_bat_init);
423 static void __exit s3c_adc_bat_exit(void)
425 platform_driver_unregister(&s3c_adc_bat_driver);
427 module_exit(s3c_adc_bat_exit);
429 MODULE_AUTHOR("Vasily Khoruzhick <anarsoul@gmail.com>");
430 MODULE_DESCRIPTION("iPAQ H1930/H1940/RX1950 battery controler driver");
431 MODULE_LICENSE("GPL");