2 * Battery driver for One Laptop Per Child board.
4 * Copyright © 2006-2010 David Woodhouse <dwmw2@infradead.org>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
11 #include <linux/kernel.h>
12 #include <linux/module.h>
13 #include <linux/types.h>
14 #include <linux/err.h>
15 #include <linux/device.h>
16 #include <linux/platform_device.h>
17 #include <linux/power_supply.h>
18 #include <linux/jiffies.h>
19 #include <linux/sched.h>
23 #define EC_BAT_VOLTAGE 0x10 /* uint16_t, *9.76/32, mV */
24 #define EC_BAT_CURRENT 0x11 /* int16_t, *15.625/120, mA */
25 #define EC_BAT_ACR 0x12 /* int16_t, *6250/15, µAh */
26 #define EC_BAT_TEMP 0x13 /* uint16_t, *100/256, °C */
27 #define EC_AMB_TEMP 0x14 /* uint16_t, *100/256, °C */
28 #define EC_BAT_STATUS 0x15 /* uint8_t, bitmask */
29 #define EC_BAT_SOC 0x16 /* uint8_t, percentage */
30 #define EC_BAT_SERIAL 0x17 /* uint8_t[6] */
31 #define EC_BAT_EEPROM 0x18 /* uint8_t adr as input, uint8_t output */
32 #define EC_BAT_ERRCODE 0x1f /* uint8_t, bitmask */
34 #define BAT_STAT_PRESENT 0x01
35 #define BAT_STAT_FULL 0x02
36 #define BAT_STAT_LOW 0x04
37 #define BAT_STAT_DESTROY 0x08
38 #define BAT_STAT_AC 0x10
39 #define BAT_STAT_CHARGING 0x20
40 #define BAT_STAT_DISCHARGING 0x40
41 #define BAT_STAT_TRICKLE 0x80
43 #define BAT_ERR_INFOFAIL 0x02
44 #define BAT_ERR_OVERVOLTAGE 0x04
45 #define BAT_ERR_OVERTEMP 0x05
46 #define BAT_ERR_GAUGESTOP 0x06
47 #define BAT_ERR_OUT_OF_CONTROL 0x07
48 #define BAT_ERR_ID_FAIL 0x09
49 #define BAT_ERR_ACR_FAIL 0x10
51 #define BAT_ADDR_MFR_TYPE 0x5F
53 /*********************************************************************
55 *********************************************************************/
57 static int olpc_ac_get_prop(struct power_supply
*psy
,
58 enum power_supply_property psp
,
59 union power_supply_propval
*val
)
65 case POWER_SUPPLY_PROP_ONLINE
:
66 ret
= olpc_ec_cmd(EC_BAT_STATUS
, NULL
, 0, &status
, 1);
70 val
->intval
= !!(status
& BAT_STAT_AC
);
79 static enum power_supply_property olpc_ac_props
[] = {
80 POWER_SUPPLY_PROP_ONLINE
,
83 static struct power_supply olpc_ac
= {
85 .type
= POWER_SUPPLY_TYPE_MAINS
,
86 .properties
= olpc_ac_props
,
87 .num_properties
= ARRAY_SIZE(olpc_ac_props
),
88 .get_property
= olpc_ac_get_prop
,
91 static char bat_serial
[17]; /* Ick */
93 static int olpc_bat_get_status(union power_supply_propval
*val
, uint8_t ec_byte
)
95 if (olpc_platform_info
.ecver
> 0x44) {
96 if (ec_byte
& (BAT_STAT_CHARGING
| BAT_STAT_TRICKLE
))
97 val
->intval
= POWER_SUPPLY_STATUS_CHARGING
;
98 else if (ec_byte
& BAT_STAT_DISCHARGING
)
99 val
->intval
= POWER_SUPPLY_STATUS_DISCHARGING
;
100 else if (ec_byte
& BAT_STAT_FULL
)
101 val
->intval
= POWER_SUPPLY_STATUS_FULL
;
103 val
->intval
= POWER_SUPPLY_STATUS_NOT_CHARGING
;
105 /* Older EC didn't report charge/discharge bits */
106 if (!(ec_byte
& BAT_STAT_AC
)) /* No AC means discharging */
107 val
->intval
= POWER_SUPPLY_STATUS_DISCHARGING
;
108 else if (ec_byte
& BAT_STAT_FULL
)
109 val
->intval
= POWER_SUPPLY_STATUS_FULL
;
110 else /* Not _necessarily_ true but EC doesn't tell all yet */
111 val
->intval
= POWER_SUPPLY_STATUS_CHARGING
;
117 static int olpc_bat_get_health(union power_supply_propval
*val
)
122 ret
= olpc_ec_cmd(EC_BAT_ERRCODE
, NULL
, 0, &ec_byte
, 1);
128 val
->intval
= POWER_SUPPLY_HEALTH_GOOD
;
131 case BAT_ERR_OVERTEMP
:
132 val
->intval
= POWER_SUPPLY_HEALTH_OVERHEAT
;
135 case BAT_ERR_OVERVOLTAGE
:
136 val
->intval
= POWER_SUPPLY_HEALTH_OVERVOLTAGE
;
139 case BAT_ERR_INFOFAIL
:
140 case BAT_ERR_OUT_OF_CONTROL
:
141 case BAT_ERR_ID_FAIL
:
142 case BAT_ERR_ACR_FAIL
:
143 val
->intval
= POWER_SUPPLY_HEALTH_UNSPEC_FAILURE
;
147 /* Eep. We don't know this failure code */
154 static int olpc_bat_get_mfr(union power_supply_propval
*val
)
159 ec_byte
= BAT_ADDR_MFR_TYPE
;
160 ret
= olpc_ec_cmd(EC_BAT_EEPROM
, &ec_byte
, 1, &ec_byte
, 1);
164 switch (ec_byte
>> 4) {
166 val
->strval
= "Gold Peak";
172 val
->strval
= "Unknown";
179 static int olpc_bat_get_tech(union power_supply_propval
*val
)
184 ec_byte
= BAT_ADDR_MFR_TYPE
;
185 ret
= olpc_ec_cmd(EC_BAT_EEPROM
, &ec_byte
, 1, &ec_byte
, 1);
189 switch (ec_byte
& 0xf) {
191 val
->intval
= POWER_SUPPLY_TECHNOLOGY_NiMH
;
194 val
->intval
= POWER_SUPPLY_TECHNOLOGY_LiFe
;
197 val
->intval
= POWER_SUPPLY_TECHNOLOGY_UNKNOWN
;
204 static int olpc_bat_get_charge_full_design(union power_supply_propval
*val
)
207 union power_supply_propval tech
;
210 ret
= olpc_bat_get_tech(&tech
);
214 ec_byte
= BAT_ADDR_MFR_TYPE
;
215 ret
= olpc_ec_cmd(EC_BAT_EEPROM
, &ec_byte
, 1, &ec_byte
, 1);
221 switch (tech
.intval
) {
222 case POWER_SUPPLY_TECHNOLOGY_NiMH
:
224 case 1: /* Gold Peak */
225 val
->intval
= 3000000*.8;
232 case POWER_SUPPLY_TECHNOLOGY_LiFe
:
234 case 1: /* Gold Peak */
235 val
->intval
= 2800000;
238 val
->intval
= 3100000;
252 static int olpc_bat_get_charge_now(union power_supply_propval
*val
)
255 union power_supply_propval full
;
258 ret
= olpc_ec_cmd(EC_BAT_SOC
, NULL
, 0, &soc
, 1);
262 ret
= olpc_bat_get_charge_full_design(&full
);
266 val
->intval
= soc
* (full
.intval
/ 100);
270 /*********************************************************************
272 *********************************************************************/
273 static int olpc_bat_get_property(struct power_supply
*psy
,
274 enum power_supply_property psp
,
275 union power_supply_propval
*val
)
282 ret
= olpc_ec_cmd(EC_BAT_STATUS
, NULL
, 0, &ec_byte
, 1);
286 /* Theoretically there's a race here -- the battery could be
287 removed immediately after we check whether it's present, and
288 then we query for some other property of the now-absent battery.
289 It doesn't matter though -- the EC will return the last-known
290 information, and it's as if we just ran that _little_ bit faster
291 and managed to read it out before the battery went away. */
292 if (!(ec_byte
& (BAT_STAT_PRESENT
| BAT_STAT_TRICKLE
)) &&
293 psp
!= POWER_SUPPLY_PROP_PRESENT
)
297 case POWER_SUPPLY_PROP_STATUS
:
298 ret
= olpc_bat_get_status(val
, ec_byte
);
302 case POWER_SUPPLY_PROP_CHARGE_TYPE
:
303 if (ec_byte
& BAT_STAT_TRICKLE
)
304 val
->intval
= POWER_SUPPLY_CHARGE_TYPE_TRICKLE
;
305 else if (ec_byte
& BAT_STAT_CHARGING
)
306 val
->intval
= POWER_SUPPLY_CHARGE_TYPE_FAST
;
308 val
->intval
= POWER_SUPPLY_CHARGE_TYPE_NONE
;
310 case POWER_SUPPLY_PROP_PRESENT
:
311 val
->intval
= !!(ec_byte
& (BAT_STAT_PRESENT
|
315 case POWER_SUPPLY_PROP_HEALTH
:
316 if (ec_byte
& BAT_STAT_DESTROY
)
317 val
->intval
= POWER_SUPPLY_HEALTH_DEAD
;
319 ret
= olpc_bat_get_health(val
);
325 case POWER_SUPPLY_PROP_MANUFACTURER
:
326 ret
= olpc_bat_get_mfr(val
);
330 case POWER_SUPPLY_PROP_TECHNOLOGY
:
331 ret
= olpc_bat_get_tech(val
);
335 case POWER_SUPPLY_PROP_VOLTAGE_AVG
:
336 case POWER_SUPPLY_PROP_VOLTAGE_NOW
:
337 ret
= olpc_ec_cmd(EC_BAT_VOLTAGE
, NULL
, 0, (void *)&ec_word
, 2);
341 val
->intval
= (s16
)be16_to_cpu(ec_word
) * 9760L / 32;
343 case POWER_SUPPLY_PROP_CURRENT_AVG
:
344 case POWER_SUPPLY_PROP_CURRENT_NOW
:
345 ret
= olpc_ec_cmd(EC_BAT_CURRENT
, NULL
, 0, (void *)&ec_word
, 2);
349 val
->intval
= (s16
)be16_to_cpu(ec_word
) * 15625L / 120;
351 case POWER_SUPPLY_PROP_CAPACITY
:
352 ret
= olpc_ec_cmd(EC_BAT_SOC
, NULL
, 0, &ec_byte
, 1);
355 val
->intval
= ec_byte
;
357 case POWER_SUPPLY_PROP_CAPACITY_LEVEL
:
358 if (ec_byte
& BAT_STAT_FULL
)
359 val
->intval
= POWER_SUPPLY_CAPACITY_LEVEL_FULL
;
360 else if (ec_byte
& BAT_STAT_LOW
)
361 val
->intval
= POWER_SUPPLY_CAPACITY_LEVEL_LOW
;
363 val
->intval
= POWER_SUPPLY_CAPACITY_LEVEL_NORMAL
;
365 case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN
:
366 ret
= olpc_bat_get_charge_full_design(val
);
370 case POWER_SUPPLY_PROP_CHARGE_NOW
:
371 ret
= olpc_bat_get_charge_now(val
);
375 case POWER_SUPPLY_PROP_TEMP
:
376 ret
= olpc_ec_cmd(EC_BAT_TEMP
, NULL
, 0, (void *)&ec_word
, 2);
380 val
->intval
= (s16
)be16_to_cpu(ec_word
) * 100 / 256;
382 case POWER_SUPPLY_PROP_TEMP_AMBIENT
:
383 ret
= olpc_ec_cmd(EC_AMB_TEMP
, NULL
, 0, (void *)&ec_word
, 2);
387 val
->intval
= (int)be16_to_cpu(ec_word
) * 100 / 256;
389 case POWER_SUPPLY_PROP_CHARGE_COUNTER
:
390 ret
= olpc_ec_cmd(EC_BAT_ACR
, NULL
, 0, (void *)&ec_word
, 2);
394 val
->intval
= (s16
)be16_to_cpu(ec_word
) * 6250 / 15;
396 case POWER_SUPPLY_PROP_SERIAL_NUMBER
:
397 ret
= olpc_ec_cmd(EC_BAT_SERIAL
, NULL
, 0, (void *)&ser_buf
, 8);
401 sprintf(bat_serial
, "%016llx", (long long)be64_to_cpu(ser_buf
));
402 val
->strval
= bat_serial
;
412 static enum power_supply_property olpc_xo1_bat_props
[] = {
413 POWER_SUPPLY_PROP_STATUS
,
414 POWER_SUPPLY_PROP_CHARGE_TYPE
,
415 POWER_SUPPLY_PROP_PRESENT
,
416 POWER_SUPPLY_PROP_HEALTH
,
417 POWER_SUPPLY_PROP_TECHNOLOGY
,
418 POWER_SUPPLY_PROP_VOLTAGE_AVG
,
419 POWER_SUPPLY_PROP_VOLTAGE_NOW
,
420 POWER_SUPPLY_PROP_CURRENT_AVG
,
421 POWER_SUPPLY_PROP_CURRENT_NOW
,
422 POWER_SUPPLY_PROP_CAPACITY
,
423 POWER_SUPPLY_PROP_CAPACITY_LEVEL
,
424 POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN
,
425 POWER_SUPPLY_PROP_CHARGE_NOW
,
426 POWER_SUPPLY_PROP_TEMP
,
427 POWER_SUPPLY_PROP_TEMP_AMBIENT
,
428 POWER_SUPPLY_PROP_MANUFACTURER
,
429 POWER_SUPPLY_PROP_SERIAL_NUMBER
,
430 POWER_SUPPLY_PROP_CHARGE_COUNTER
,
433 /* XO-1.5 does not have ambient temperature property */
434 static enum power_supply_property olpc_xo15_bat_props
[] = {
435 POWER_SUPPLY_PROP_STATUS
,
436 POWER_SUPPLY_PROP_CHARGE_TYPE
,
437 POWER_SUPPLY_PROP_PRESENT
,
438 POWER_SUPPLY_PROP_HEALTH
,
439 POWER_SUPPLY_PROP_TECHNOLOGY
,
440 POWER_SUPPLY_PROP_VOLTAGE_AVG
,
441 POWER_SUPPLY_PROP_VOLTAGE_NOW
,
442 POWER_SUPPLY_PROP_CURRENT_AVG
,
443 POWER_SUPPLY_PROP_CURRENT_NOW
,
444 POWER_SUPPLY_PROP_CAPACITY
,
445 POWER_SUPPLY_PROP_CAPACITY_LEVEL
,
446 POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN
,
447 POWER_SUPPLY_PROP_CHARGE_NOW
,
448 POWER_SUPPLY_PROP_TEMP
,
449 POWER_SUPPLY_PROP_MANUFACTURER
,
450 POWER_SUPPLY_PROP_SERIAL_NUMBER
,
451 POWER_SUPPLY_PROP_CHARGE_COUNTER
,
454 /* EEPROM reading goes completely around the power_supply API, sadly */
456 #define EEPROM_START 0x20
457 #define EEPROM_END 0x80
458 #define EEPROM_SIZE (EEPROM_END - EEPROM_START)
460 static ssize_t
olpc_bat_eeprom_read(struct file
*filp
, struct kobject
*kobj
,
461 struct bin_attribute
*attr
, char *buf
, loff_t off
, size_t count
)
467 if (off
>= EEPROM_SIZE
)
469 if (off
+ count
> EEPROM_SIZE
)
470 count
= EEPROM_SIZE
- off
;
472 for (i
= 0; i
< count
; i
++) {
473 ec_byte
= EEPROM_START
+ off
+ i
;
474 ret
= olpc_ec_cmd(EC_BAT_EEPROM
, &ec_byte
, 1, &buf
[i
], 1);
476 pr_err("olpc-battery: "
477 "EC_BAT_EEPROM cmd @ 0x%x failed - %d!\n",
486 static struct bin_attribute olpc_bat_eeprom
= {
492 .read
= olpc_bat_eeprom_read
,
495 /* Allow userspace to see the specific error value pulled from the EC */
497 static ssize_t
olpc_bat_error_read(struct device
*dev
,
498 struct device_attribute
*attr
, char *buf
)
503 ret
= olpc_ec_cmd(EC_BAT_ERRCODE
, NULL
, 0, &ec_byte
, 1);
507 return sprintf(buf
, "%d\n", ec_byte
);
510 static struct device_attribute olpc_bat_error
= {
515 .show
= olpc_bat_error_read
,
518 /*********************************************************************
520 *********************************************************************/
522 static struct power_supply olpc_bat
= {
523 .name
= "olpc-battery",
524 .get_property
= olpc_bat_get_property
,
528 void olpc_battery_trigger_uevent(unsigned long cause
)
530 if (cause
& EC_SCI_SRC_ACPWR
)
531 kobject_uevent(&olpc_ac
.dev
->kobj
, KOBJ_CHANGE
);
532 if (cause
& (EC_SCI_SRC_BATERR
|EC_SCI_SRC_BATSOC
|EC_SCI_SRC_BATTERY
))
533 kobject_uevent(&olpc_bat
.dev
->kobj
, KOBJ_CHANGE
);
536 static int olpc_battery_suspend(struct platform_device
*pdev
,
539 if (device_may_wakeup(olpc_ac
.dev
))
540 olpc_ec_wakeup_set(EC_SCI_SRC_ACPWR
);
542 olpc_ec_wakeup_clear(EC_SCI_SRC_ACPWR
);
544 if (device_may_wakeup(olpc_bat
.dev
))
545 olpc_ec_wakeup_set(EC_SCI_SRC_BATTERY
| EC_SCI_SRC_BATSOC
546 | EC_SCI_SRC_BATERR
);
548 olpc_ec_wakeup_clear(EC_SCI_SRC_BATTERY
| EC_SCI_SRC_BATSOC
549 | EC_SCI_SRC_BATERR
);
554 static int __devinit
olpc_battery_probe(struct platform_device
*pdev
)
560 * We've seen a number of EC protocol changes; this driver requires
561 * the latest EC protocol, supported by 0x44 and above.
563 if (olpc_platform_info
.ecver
< 0x44) {
564 printk(KERN_NOTICE
"OLPC EC version 0x%02x too old for "
565 "battery driver.\n", olpc_platform_info
.ecver
);
569 ret
= olpc_ec_cmd(EC_BAT_STATUS
, NULL
, 0, &status
, 1);
573 /* Ignore the status. It doesn't actually matter */
575 ret
= power_supply_register(&pdev
->dev
, &olpc_ac
);
579 if (olpc_board_at_least(olpc_board_pre(0xd0))) { /* XO-1.5 */
580 olpc_bat
.properties
= olpc_xo15_bat_props
;
581 olpc_bat
.num_properties
= ARRAY_SIZE(olpc_xo15_bat_props
);
583 olpc_bat
.properties
= olpc_xo1_bat_props
;
584 olpc_bat
.num_properties
= ARRAY_SIZE(olpc_xo1_bat_props
);
587 ret
= power_supply_register(&pdev
->dev
, &olpc_bat
);
591 ret
= device_create_bin_file(olpc_bat
.dev
, &olpc_bat_eeprom
);
595 ret
= device_create_file(olpc_bat
.dev
, &olpc_bat_error
);
599 if (olpc_ec_wakeup_available()) {
600 device_set_wakeup_capable(olpc_ac
.dev
, true);
601 device_set_wakeup_capable(olpc_bat
.dev
, true);
607 device_remove_bin_file(olpc_bat
.dev
, &olpc_bat_eeprom
);
609 power_supply_unregister(&olpc_bat
);
611 power_supply_unregister(&olpc_ac
);
615 static int __devexit
olpc_battery_remove(struct platform_device
*pdev
)
617 device_remove_file(olpc_bat
.dev
, &olpc_bat_error
);
618 device_remove_bin_file(olpc_bat
.dev
, &olpc_bat_eeprom
);
619 power_supply_unregister(&olpc_bat
);
620 power_supply_unregister(&olpc_ac
);
624 static const struct of_device_id olpc_battery_ids
[] __devinitconst
= {
625 { .compatible
= "olpc,xo1-battery" },
628 MODULE_DEVICE_TABLE(of
, olpc_battery_ids
);
630 static struct platform_driver olpc_battery_drv
= {
632 .name
= "olpc-battery",
633 .owner
= THIS_MODULE
,
634 .of_match_table
= olpc_battery_ids
,
636 .probe
= olpc_battery_probe
,
637 .remove
= __devexit_p(olpc_battery_remove
),
638 .suspend
= olpc_battery_suspend
,
641 static int __init
olpc_bat_init(void)
643 return platform_driver_register(&olpc_battery_drv
);
645 module_init(olpc_bat_init
);
647 static void __exit
olpc_bat_exit(void)
649 platform_driver_unregister(&olpc_battery_drv
);
651 module_exit(olpc_bat_exit
);
653 MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
654 MODULE_LICENSE("GPL");
655 MODULE_DESCRIPTION("Battery driver for One Laptop Per Child 'XO' machine");