pull master
[hh.org.git] / drivers / hwmon / battery / h2200_battery.c
blob26a5c865fa9d6334538a4f5ec0c2db31a03cc83f
1 /*
2 * Battery driver lowlevel interface for iPAQ H2200 series
4 * Copyright (c) 2004 Matt Reimer
5 * 2004 Szabolcs Gyurko
7 * Use consistent with the GNU GPL is permitted,
8 * provided that this copyright notice is
9 * preserved in its entirety in all copies and derived works.
11 * Author: Matt Reimer <mreimer@vpop.net>
12 * April 2004, 2005
14 * Szabolcs Gyurko <szabolcs.gyurko@tlt.hu>
15 * September 2004
17 * To do:
19 * - While suspended, periodically check for battery full and update
20 * the LED status.
21 * - Should we turn off the charger GPIO when the battery is full?
22 * - Is the battery status caching necessary?
25 #include <linux/module.h>
26 #include <linux/version.h>
27 #include <linux/init.h>
28 #include <linux/fs.h>
29 #include <linux/interrupt.h>
30 #include <linux/irq.h>
31 #include <linux/sched.h>
32 #include <linux/pm.h>
33 #include <linux/sysctl.h>
34 #include <linux/proc_fs.h>
35 #include <linux/delay.h>
36 #include <linux/jiffies.h>
37 #include <linux/platform_device.h>
38 #include <linux/battery.h>
39 #include <linux/leds.h>
41 #include <asm/io.h>
42 #include <asm/apm.h>
43 #include <asm/arch/hardware.h>
44 #include <asm/arch/pxa-regs.h>
46 #include <asm/arch-pxa/h2200-asic.h>
47 #include <asm/arch-pxa/h2200-gpio.h>
48 #include <asm/arch-pxa/h2200-leds.h>
50 #include "../../w1/w1.h"
51 #include "../../w1/slaves/w1_ds2760.h"
53 #undef USE_LEDS_API
55 #define H2200_BATTERY_MAX_VOLTAGE 4400 /* My measurements */
56 #define H2200_BATTERY_MIN_VOLTAGE 0 /* Maybe incorrect */
57 #define H2200_BATTERY_MAX_CURRENT 950 /* MUST BE MEASURED */
58 #define H2200_BATTERY_MIN_CURRENT -950 /* Maybe incorrect */
59 #define H2200_BATTERY_MIN_CHARGE 0 /* Maybe incorrect */
61 #define BATTERY_CHECK_INTERVAL (HZ * 60) /* every 60 seconds */
63 #define POWER_NONE 0
64 #define POWER_AC 1
66 /* AC present? */
67 static int power_status;
69 /* w1_samcop device */
70 static struct workqueue_struct *probe_q;
71 static struct work_struct probe_work;
73 /* DS2760 device */
74 struct device *ds2760_dev = NULL;
76 /* Cache the battery status for this many seconds. */
77 #define STATUS_CACHE_TIME (HZ * 1)
78 static struct ds2760_status battery_status;
80 static int battery_charge_state;
81 #define BATTERY_STATE_UNKNOWN 0
82 #define BATTERY_DISCHARGING 1
83 #define BATTERY_CHARGING 2
84 #define BATTERY_FULL 3
86 /* battery monitor */
87 static struct timer_list monitor_timer;
88 static struct workqueue_struct *monitor_q;
89 static struct work_struct monitor_work;
91 #if defined(CONFIG_APM) || defined(CONFIG_APM_MODULE)
92 /* original APM hook */
93 static void (*apm_get_power_status_orig)(struct apm_power_info *info);
94 #endif
97 int
98 h2200_battery_read_status(void)
100 if (jiffies < (battery_status.update_time + STATUS_CACHE_TIME))
101 return 0;
103 if (!ds2760_dev)
104 return 0;
106 if (!w1_ds2760_status(ds2760_dev, &battery_status)) {
107 printk("call to w1_ds2760_status failed (0x%08x)\n",
108 (unsigned int)ds2760_dev);
109 return 1;
112 return 0;
115 #if defined(CONFIG_APM) || defined(CONFIG_APM_MODULE)
116 static void
117 h2200_apm_get_power_status (struct apm_power_info *info)
119 h2200_battery_read_status();
121 info->ac_line_status = (power_status != POWER_NONE) ?
122 APM_AC_ONLINE : APM_AC_OFFLINE;
124 if (info->ac_line_status == APM_AC_ONLINE && current > 0) {
125 info->battery_status = APM_BATTERY_STATUS_CHARGING;
126 } else {
128 info->battery_status = battery_status.accum_current_mAh >
129 ((battery_status.rated_capacity * 75) / 100) ?
130 APM_BATTERY_STATUS_HIGH :
131 battery_status.accum_current_mAh >
132 ((battery_status.rated_capacity* 25) / 100) ?
133 APM_BATTERY_STATUS_LOW : APM_BATTERY_STATUS_CRITICAL;
136 info->battery_flag = info->battery_status;
138 if (battery_status.rated_capacity)
139 info->battery_life = (battery_status.accum_current_mAh * 100) /
140 battery_status.rated_capacity;
142 if (info->battery_life > 100)
143 info->battery_life = 100;
144 if (info->battery_life < 0)
145 info->battery_life = 0;
147 if (battery_status.current_mA) {
148 info->time = - ((battery_status.accum_current_mAh * 6000) /
149 battery_status.current_mA) / 100;
150 info->units = APM_UNITS_MINS;
151 } else
152 info->units = APM_UNITS_UNKNOWN;
154 #endif
157 h2200_battery_get_max_voltage(struct battery *bat)
159 return H2200_BATTERY_MAX_VOLTAGE;
163 h2200_battery_get_min_voltage(struct battery *bat)
165 return H2200_BATTERY_MIN_VOLTAGE;
169 h2200_battery_get_voltage(struct battery *bat)
171 h2200_battery_read_status();
172 return battery_status.voltage_mV;
176 h2200_battery_get_max_current(struct battery *bat)
178 return H2200_BATTERY_MAX_CURRENT;
182 h2200_battery_get_min_current(struct battery *bat)
184 return H2200_BATTERY_MIN_CURRENT;
188 h2200_battery_get_current(struct battery *bat)
190 h2200_battery_read_status();
191 return battery_status.current_mA;
195 h2200_battery_get_max_charge(struct battery *bat)
197 h2200_battery_read_status();
198 return battery_status.rated_capacity;
202 h2200_battery_get_min_charge(struct battery *bat)
204 return H2200_BATTERY_MIN_CHARGE;
208 h2200_battery_get_charge(struct battery *bat)
210 h2200_battery_read_status();
211 return battery_status.accum_current_mAh;
215 h2200_battery_get_temp(struct battery *bat)
217 h2200_battery_read_status();
218 return battery_status.temp_C;
222 h2200_battery_get_status(struct battery *bat)
224 int status;
226 status = (GET_H2200_GPIO(AC_IN_N)) ?
227 BATTERY_STATUS_NOT_CHARGING : BATTERY_STATUS_CHARGING;
229 return status;
232 void
233 h2200_battery_update_status(void *data)
235 static int full_counter;
237 h2200_battery_read_status();
239 if (battery_charge_state == BATTERY_STATE_UNKNOWN)
240 full_counter = 0;
242 /* Set the proper charging rate. */
243 power_status = GET_H2200_GPIO(AC_IN_N) ? 0 : 1;
244 if (power_status) {
246 SET_H2200_GPIO(CHG_EN, 1);
248 if (battery_status.current_mA > 10) {
250 battery_charge_state = BATTERY_CHARGING;
251 full_counter = 0;
253 } else if (battery_status.current_mA < 10 &&
254 battery_charge_state != BATTERY_FULL) {
256 /* Don't consider the battery to be full unless
257 * we've seen the current < 10 mA at least two
258 * consecutive times. */
260 full_counter++;
262 if (full_counter < 2)
263 battery_charge_state = BATTERY_CHARGING;
264 else {
266 unsigned char acr[2];
268 acr[0] = (battery_status.rated_capacity * 4) >> 8;
269 acr[1] = (battery_status.rated_capacity * 4) & 0xff;
271 if (w1_ds2760_write(ds2760_dev, acr,
272 DS2760_CURRENT_ACCUM_MSB, 2) < 2)
273 printk(KERN_ERR "ACR reset failed\n");
274 battery_charge_state = BATTERY_FULL;
278 if (battery_charge_state == BATTERY_CHARGING) {
280 /* Blink the LED while plugged in and charging. */
281 #ifdef USE_LEDS_API
282 //leds_set_brightness(h2200_power_led, 100);
283 leds_set_frequency(h2200_power_led, 500);
284 #else
285 h2200_set_led(0, 4, 16);
286 #endif
287 } else {
289 /* Set the LED solid while on AC and not charging. */
290 #ifdef USE_LEDS_API
291 //leds_set_brightness(h2200_power_led, 100);
292 leds_set_frequency(h2200_power_led, 0);
293 #else
294 h2200_set_led(0, 8, 8);
295 #endif
298 } else {
300 battery_charge_state = BATTERY_DISCHARGING;
301 full_counter = 0;
303 SET_H2200_GPIO(CHG_EN, 0);
305 /* Turn off the LED while unplugged from AC. */
306 #ifdef USE_LEDS_API
307 //leds_set_brightness(h2200_power_led, 0);
308 leds_set_frequency(h2200_power_led, 0);
309 #else
310 h2200_set_led(0, 0, 0);
311 #endif
316 static irqreturn_t
317 h2200_ac_plug_isr (int isr, void *data, struct pt_regs *regs)
320 /* Update the LED status right away, for timely feedback. */
321 power_status = GET_H2200_GPIO(AC_IN_N) ? 0 : 1;
322 if (power_status) {
323 /* Start off with a solid LED; if we're charging,
324 the LED state will be updated when the queued work runs. */
325 #ifdef USE_LEDS_API
326 //leds_set_brightness(h2200_power_led, 100);
327 leds_set_frequency(h2200_power_led, 0);
328 #else
329 h2200_set_led(0, 8, 8);
330 #endif
331 } else {
332 #ifdef USE_LEDS_API
333 //leds_set_brightness(h2200_power_led, 0);
334 leds_set_frequency(h2200_power_led, 0);
335 #else
336 h2200_set_led(0, 0, 0);
337 #endif
340 /* Force the data to be re-read. */
341 battery_status.update_time = 0;
343 /* Wait a few seconds to let the DS2760 update its current reading. */
344 queue_delayed_work(monitor_q, &monitor_work, HZ * 7);
346 return IRQ_HANDLED;
349 static irqreturn_t
350 h2200_battery_door_isr (int isr, void *data, struct pt_regs *regs)
352 int door_open = GET_H2200_GPIO(BATT_DOOR_N) ? 0 : 1;
354 if (door_open)
355 printk(KERN_ERR "battery door opened!\n");
356 else
357 printk(KERN_ERR "battery door closed\n");
359 return IRQ_HANDLED;
362 static struct battery h2200_battery = {
363 .name = "h2200 battery",
364 .id = "battery0",
365 .get_voltage = h2200_battery_get_voltage,
366 .get_min_voltage = h2200_battery_get_min_voltage,
367 .get_max_voltage = h2200_battery_get_max_voltage,
368 .get_current = h2200_battery_get_current,
369 .get_min_current = h2200_battery_get_min_current,
370 .get_max_current = h2200_battery_get_max_current,
371 .get_charge = h2200_battery_get_charge,
372 .get_min_charge = h2200_battery_get_min_charge,
373 .get_max_charge = h2200_battery_get_max_charge,
374 .get_temp = h2200_battery_get_temp,
375 .get_status = h2200_battery_get_status,
378 #ifdef MODULE
379 static void
380 h2200_battery_class_release(struct class_device *dev)
384 static void
385 h2200_battery_class_class_release(struct class *class)
388 #endif
390 /* -------------------------------------------------------------------- */
392 static int
393 h2200_battery_match_callback(struct device *dev, void *data)
395 struct w1_slave *sl;
397 if (!(dev->driver && dev->driver->name &&
398 (strcmp(dev->driver->name, "w1_slave_driver") == 0)))
399 return 0;
401 sl = container_of(dev, struct w1_slave, dev);
403 /* DS2760 w1 slave device names begin with the family number 0x30. */
404 if (strncmp(sl->name, "30-", 3) != 0)
405 return 0;
407 return 1;
410 void
411 h2200_battery_probe_work(void *data)
413 struct bus_type *bus;
415 /* Get the battery w1 slave device. */
416 bus = find_bus("w1");
417 if (bus)
418 ds2760_dev = bus_find_device(bus, NULL, NULL,
419 h2200_battery_match_callback);
421 if (!ds2760_dev) {
422 /* No DS2760 device found; try again later. */
423 queue_delayed_work(probe_q, &probe_work, HZ * 5);
424 return;
429 static void
430 h2200_battery_timer(unsigned long data)
432 queue_work(monitor_q, &monitor_work);
433 mod_timer(&monitor_timer, jiffies + BATTERY_CHECK_INTERVAL);
436 static int
437 h2200_battery_probe(struct device *dev)
439 printk("Battery interface for iPAQ h2200 series - (c) 2004 Szabolcs Gyurko\n");
441 battery_charge_state = BATTERY_STATE_UNKNOWN;
443 /* Install an interrupt handler for AC plug/unplug. */
444 set_irq_type(H2200_IRQ(AC_IN_N), IRQT_BOTHEDGE);
445 request_irq(H2200_IRQ(AC_IN_N), h2200_ac_plug_isr, SA_SAMPLE_RANDOM,
446 "AC plug", NULL);
448 /* Install an interrupt handler for battery door open/close. */
449 set_irq_type(H2200_IRQ(BATT_DOOR_N), IRQT_BOTHEDGE);
450 request_irq(H2200_IRQ(BATT_DOOR_N), h2200_battery_door_isr,
451 SA_SAMPLE_RANDOM, "battery door", NULL);
453 /* Create a workqueue in which the battery monitor will run. */
454 monitor_q = create_singlethread_workqueue("battmon");
455 INIT_WORK(&monitor_work, h2200_battery_update_status, NULL);
457 /* Create a timer to run the battery monitor every
458 BATTERY_CHECK_INTERVAL seconds. */
459 init_timer(&monitor_timer);
460 monitor_timer.function = h2200_battery_timer;
461 monitor_timer.data = 0;
463 /* Start the first monitor task a few seconds from now. */
464 mod_timer(&monitor_timer, jiffies + HZ * 10);
466 #if defined(CONFIG_APM) || defined(CONFIG_APM_MODULE)
467 apm_get_power_status = h2200_apm_get_power_status;
468 #endif
470 #ifdef USE_LEDS_API
471 leds_acquire(h2200_power_led); /* XXX check return val */
472 #endif
474 /* Get the DS2760 W1 device. */
475 probe_q = create_singlethread_workqueue("battmonprb");
476 INIT_WORK(&probe_work, h2200_battery_probe_work, NULL);
477 h2200_battery_probe_work(NULL);
479 return 0;
482 static void
483 h2200_battery_shutdown(struct device *dev)
485 #if defined(CONFIG_APM) || defined(CONFIG_APM_MODULE)
486 apm_get_power_status = apm_get_power_status_orig;
487 #endif
489 del_timer_sync(&monitor_timer);
491 cancel_delayed_work(&monitor_work);
492 flush_workqueue(monitor_q);
493 destroy_workqueue(monitor_q);
495 cancel_delayed_work(&probe_work);
496 flush_workqueue(probe_q);
497 destroy_workqueue(probe_q);
499 if (ds2760_dev)
500 put_device(ds2760_dev);
502 free_irq(H2200_IRQ(AC_IN_N), NULL);
503 free_irq(H2200_IRQ(BATT_DOOR_N), NULL);
505 #ifdef USE_LEDS_API
506 //leds_set_brightness(h2200_power_led, 0);
507 leds_set_frequency(h2200_power_led, 0);
508 leds_release(h2200_power_led);
509 #else
510 h2200_set_led(0, 0, 0);
511 #endif
514 static int
515 h2200_battery_remove(struct device *dev)
517 h2200_battery_shutdown(dev);
518 return 0;
521 static int
522 h2200_battery_suspend(struct device *dev, pm_message_t state)
524 cancel_delayed_work(&monitor_work);
525 flush_workqueue(monitor_q);
527 return 0;
530 static int
531 h2200_battery_resume(struct device *dev)
533 battery_charge_state = BATTERY_STATUS_UNKNOWN;
534 queue_work(monitor_q, &monitor_work);
535 mod_timer(&monitor_timer, jiffies + HZ * 5);
537 return 0;
540 static struct device_driver h2200_battery_driver = {
541 .name = "h2200 battery",
542 .bus = &platform_bus_type,
543 .probe = h2200_battery_probe,
544 .remove = h2200_battery_remove,
545 .shutdown = h2200_battery_shutdown,
546 .suspend = h2200_battery_suspend,
547 .resume = h2200_battery_resume
550 static int __init
551 h2200_battery_init(void)
553 int retval;
555 retval = driver_register(&h2200_battery_driver);
556 if (retval)
557 return retval;
559 retval = battery_class_register(&h2200_battery);
560 #ifdef MODULE
561 h2200_battery.class_dev.class->release = h2200_battery_class_release;
562 h2200_battery.class_dev.class->class_release = h2200_battery_class_class_release;
563 #endif
565 return retval;
568 static void __exit h2200_battery_exit(void)
570 battery_class_unregister(&h2200_battery);
571 driver_unregister(&h2200_battery_driver);
574 module_init(h2200_battery_init);
575 module_exit(h2200_battery_exit);
577 MODULE_LICENSE("GPL");
578 MODULE_AUTHOR("Szabolcs Gyurko <szabolcs.gyurko@tlt.hu>");
579 MODULE_DESCRIPTION("HP iPAQ h2200 battery driver");
582 * Local Variables:
583 * mode:c
584 * c-style:"K&R"
585 * c-basic-offset:8
586 * End: