2 * Battery driver lowlevel interface for iPAQ H2200 series
4 * Copyright (c) 2004 Matt Reimer
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>
14 * Szabolcs Gyurko <szabolcs.gyurko@tlt.hu>
19 * - While suspended, periodically check for battery full and update
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>
29 #include <linux/interrupt.h>
30 #include <linux/irq.h>
31 #include <linux/sched.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>
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"
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 */
67 static int power_status
;
69 /* w1_samcop device */
70 static struct workqueue_struct
*probe_q
;
71 static struct work_struct probe_work
;
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
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
);
98 h2200_battery_read_status(void)
100 if (jiffies
< (battery_status
.update_time
+ STATUS_CACHE_TIME
))
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
);
115 #if defined(CONFIG_APM) || defined(CONFIG_APM_MODULE)
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
;
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
;
152 info
->units
= APM_UNITS_UNKNOWN
;
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
)
226 status
= (GET_H2200_GPIO(AC_IN_N
)) ?
227 BATTERY_STATUS_NOT_CHARGING
: BATTERY_STATUS_CHARGING
;
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
)
242 /* Set the proper charging rate. */
243 power_status
= GET_H2200_GPIO(AC_IN_N
) ? 0 : 1;
246 SET_H2200_GPIO(CHG_EN
, 1);
248 if (battery_status
.current_mA
> 10) {
250 battery_charge_state
= BATTERY_CHARGING
;
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. */
262 if (full_counter
< 2)
263 battery_charge_state
= BATTERY_CHARGING
;
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. */
282 //leds_set_brightness(h2200_power_led, 100);
283 leds_set_frequency(h2200_power_led
, 500);
285 h2200_set_led(0, 4, 16);
289 /* Set the LED solid while on AC and not charging. */
291 //leds_set_brightness(h2200_power_led, 100);
292 leds_set_frequency(h2200_power_led
, 0);
294 h2200_set_led(0, 8, 8);
300 battery_charge_state
= BATTERY_DISCHARGING
;
303 SET_H2200_GPIO(CHG_EN
, 0);
305 /* Turn off the LED while unplugged from AC. */
307 //leds_set_brightness(h2200_power_led, 0);
308 leds_set_frequency(h2200_power_led
, 0);
310 h2200_set_led(0, 0, 0);
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;
323 /* Start off with a solid LED; if we're charging,
324 the LED state will be updated when the queued work runs. */
326 //leds_set_brightness(h2200_power_led, 100);
327 leds_set_frequency(h2200_power_led
, 0);
329 h2200_set_led(0, 8, 8);
333 //leds_set_brightness(h2200_power_led, 0);
334 leds_set_frequency(h2200_power_led
, 0);
336 h2200_set_led(0, 0, 0);
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);
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;
355 printk(KERN_ERR
"battery door opened!\n");
357 printk(KERN_ERR
"battery door closed\n");
362 static struct battery h2200_battery
= {
363 .name
= "h2200 battery",
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
,
380 h2200_battery_class_release(struct class_device
*dev
)
385 h2200_battery_class_class_release(struct class *class)
390 /* -------------------------------------------------------------------- */
393 h2200_battery_match_callback(struct device
*dev
, void *data
)
397 if (!(dev
->driver
&& dev
->driver
->name
&&
398 (strcmp(dev
->driver
->name
, "w1_slave_driver") == 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)
411 h2200_battery_probe_work(void *data
)
413 struct bus_type
*bus
;
415 /* Get the battery w1 slave device. */
416 bus
= find_bus("w1");
418 ds2760_dev
= bus_find_device(bus
, NULL
, NULL
,
419 h2200_battery_match_callback
);
422 /* No DS2760 device found; try again later. */
423 queue_delayed_work(probe_q
, &probe_work
, HZ
* 5);
430 h2200_battery_timer(unsigned long data
)
432 queue_work(monitor_q
, &monitor_work
);
433 mod_timer(&monitor_timer
, jiffies
+ BATTERY_CHECK_INTERVAL
);
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
,
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
;
471 leds_acquire(h2200_power_led
); /* XXX check return val */
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
);
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
;
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
);
500 put_device(ds2760_dev
);
502 free_irq(H2200_IRQ(AC_IN_N
), NULL
);
503 free_irq(H2200_IRQ(BATT_DOOR_N
), NULL
);
506 //leds_set_brightness(h2200_power_led, 0);
507 leds_set_frequency(h2200_power_led
, 0);
508 leds_release(h2200_power_led
);
510 h2200_set_led(0, 0, 0);
515 h2200_battery_remove(struct device
*dev
)
517 h2200_battery_shutdown(dev
);
522 h2200_battery_suspend(struct device
*dev
, pm_message_t state
)
524 cancel_delayed_work(&monitor_work
);
525 flush_workqueue(monitor_q
);
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);
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
551 h2200_battery_init(void)
555 retval
= driver_register(&h2200_battery_driver
);
559 retval
= battery_class_register(&h2200_battery
);
561 h2200_battery
.class_dev
.class->release
= h2200_battery_class_release
;
562 h2200_battery
.class_dev
.class->class_release
= h2200_battery_class_class_release
;
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");