2 * Battery driver for One Laptop Per Child board.
4 * Copyright © 2006 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 /*********************************************************************
206 *********************************************************************/
207 static int olpc_bat_get_property(struct power_supply
*psy
,
208 enum power_supply_property psp
,
209 union power_supply_propval
*val
)
216 ret
= olpc_ec_cmd(EC_BAT_STATUS
, NULL
, 0, &ec_byte
, 1);
220 /* Theoretically there's a race here -- the battery could be
221 removed immediately after we check whether it's present, and
222 then we query for some other property of the now-absent battery.
223 It doesn't matter though -- the EC will return the last-known
224 information, and it's as if we just ran that _little_ bit faster
225 and managed to read it out before the battery went away. */
226 if (!(ec_byte
& (BAT_STAT_PRESENT
| BAT_STAT_TRICKLE
)) &&
227 psp
!= POWER_SUPPLY_PROP_PRESENT
)
231 case POWER_SUPPLY_PROP_STATUS
:
232 ret
= olpc_bat_get_status(val
, ec_byte
);
236 case POWER_SUPPLY_PROP_CHARGE_TYPE
:
237 if (ec_byte
& BAT_STAT_TRICKLE
)
238 val
->intval
= POWER_SUPPLY_CHARGE_TYPE_TRICKLE
;
239 else if (ec_byte
& BAT_STAT_CHARGING
)
240 val
->intval
= POWER_SUPPLY_CHARGE_TYPE_FAST
;
242 val
->intval
= POWER_SUPPLY_CHARGE_TYPE_NONE
;
244 case POWER_SUPPLY_PROP_PRESENT
:
245 val
->intval
= !!(ec_byte
& (BAT_STAT_PRESENT
|
249 case POWER_SUPPLY_PROP_HEALTH
:
250 if (ec_byte
& BAT_STAT_DESTROY
)
251 val
->intval
= POWER_SUPPLY_HEALTH_DEAD
;
253 ret
= olpc_bat_get_health(val
);
259 case POWER_SUPPLY_PROP_MANUFACTURER
:
260 ret
= olpc_bat_get_mfr(val
);
264 case POWER_SUPPLY_PROP_TECHNOLOGY
:
265 ret
= olpc_bat_get_tech(val
);
269 case POWER_SUPPLY_PROP_VOLTAGE_AVG
:
270 ret
= olpc_ec_cmd(EC_BAT_VOLTAGE
, NULL
, 0, (void *)&ec_word
, 2);
274 val
->intval
= (s16
)be16_to_cpu(ec_word
) * 9760L / 32;
276 case POWER_SUPPLY_PROP_CURRENT_AVG
:
277 ret
= olpc_ec_cmd(EC_BAT_CURRENT
, NULL
, 0, (void *)&ec_word
, 2);
281 val
->intval
= (s16
)be16_to_cpu(ec_word
) * 15625L / 120;
283 case POWER_SUPPLY_PROP_CAPACITY
:
284 ret
= olpc_ec_cmd(EC_BAT_SOC
, NULL
, 0, &ec_byte
, 1);
287 val
->intval
= ec_byte
;
289 case POWER_SUPPLY_PROP_CAPACITY_LEVEL
:
290 if (ec_byte
& BAT_STAT_FULL
)
291 val
->intval
= POWER_SUPPLY_CAPACITY_LEVEL_FULL
;
292 else if (ec_byte
& BAT_STAT_LOW
)
293 val
->intval
= POWER_SUPPLY_CAPACITY_LEVEL_LOW
;
295 val
->intval
= POWER_SUPPLY_CAPACITY_LEVEL_NORMAL
;
297 case POWER_SUPPLY_PROP_TEMP
:
298 ret
= olpc_ec_cmd(EC_BAT_TEMP
, NULL
, 0, (void *)&ec_word
, 2);
302 val
->intval
= (s16
)be16_to_cpu(ec_word
) * 100 / 256;
304 case POWER_SUPPLY_PROP_TEMP_AMBIENT
:
305 ret
= olpc_ec_cmd(EC_AMB_TEMP
, NULL
, 0, (void *)&ec_word
, 2);
309 val
->intval
= (int)be16_to_cpu(ec_word
) * 100 / 256;
311 case POWER_SUPPLY_PROP_CHARGE_COUNTER
:
312 ret
= olpc_ec_cmd(EC_BAT_ACR
, NULL
, 0, (void *)&ec_word
, 2);
316 val
->intval
= (s16
)be16_to_cpu(ec_word
) * 6250 / 15;
318 case POWER_SUPPLY_PROP_SERIAL_NUMBER
:
319 ret
= olpc_ec_cmd(EC_BAT_SERIAL
, NULL
, 0, (void *)&ser_buf
, 8);
323 sprintf(bat_serial
, "%016llx", (long long)be64_to_cpu(ser_buf
));
324 val
->strval
= bat_serial
;
334 static enum power_supply_property olpc_bat_props
[] = {
335 POWER_SUPPLY_PROP_STATUS
,
336 POWER_SUPPLY_PROP_CHARGE_TYPE
,
337 POWER_SUPPLY_PROP_PRESENT
,
338 POWER_SUPPLY_PROP_HEALTH
,
339 POWER_SUPPLY_PROP_TECHNOLOGY
,
340 POWER_SUPPLY_PROP_VOLTAGE_AVG
,
341 POWER_SUPPLY_PROP_CURRENT_AVG
,
342 POWER_SUPPLY_PROP_CAPACITY
,
343 POWER_SUPPLY_PROP_CAPACITY_LEVEL
,
344 POWER_SUPPLY_PROP_TEMP
,
345 POWER_SUPPLY_PROP_TEMP_AMBIENT
,
346 POWER_SUPPLY_PROP_MANUFACTURER
,
347 POWER_SUPPLY_PROP_SERIAL_NUMBER
,
348 POWER_SUPPLY_PROP_CHARGE_COUNTER
,
351 /* EEPROM reading goes completely around the power_supply API, sadly */
353 #define EEPROM_START 0x20
354 #define EEPROM_END 0x80
355 #define EEPROM_SIZE (EEPROM_END - EEPROM_START)
357 static ssize_t
olpc_bat_eeprom_read(struct kobject
*kobj
,
358 struct bin_attribute
*attr
, char *buf
, loff_t off
, size_t count
)
364 if (off
>= EEPROM_SIZE
)
366 if (off
+ count
> EEPROM_SIZE
)
367 count
= EEPROM_SIZE
- off
;
369 for (i
= 0; i
< count
; i
++) {
370 ec_byte
= EEPROM_START
+ off
+ i
;
371 ret
= olpc_ec_cmd(EC_BAT_EEPROM
, &ec_byte
, 1, &buf
[i
], 1);
373 pr_err("olpc-battery: "
374 "EC_BAT_EEPROM cmd @ 0x%x failed - %d!\n",
383 static struct bin_attribute olpc_bat_eeprom
= {
387 .owner
= THIS_MODULE
,
390 .read
= olpc_bat_eeprom_read
,
393 /* Allow userspace to see the specific error value pulled from the EC */
395 static ssize_t
olpc_bat_error_read(struct device
*dev
,
396 struct device_attribute
*attr
, char *buf
)
401 ret
= olpc_ec_cmd(EC_BAT_ERRCODE
, NULL
, 0, &ec_byte
, 1);
405 return sprintf(buf
, "%d\n", ec_byte
);
408 static struct device_attribute olpc_bat_error
= {
413 .show
= olpc_bat_error_read
,
416 /*********************************************************************
418 *********************************************************************/
420 static struct platform_device
*bat_pdev
;
422 static struct power_supply olpc_bat
= {
423 .properties
= olpc_bat_props
,
424 .num_properties
= ARRAY_SIZE(olpc_bat_props
),
425 .get_property
= olpc_bat_get_property
,
429 void olpc_battery_trigger_uevent(unsigned long cause
)
431 if (cause
& EC_SCI_SRC_ACPWR
)
432 kobject_uevent(&olpc_ac
.dev
->kobj
, KOBJ_CHANGE
);
433 if (cause
& (EC_SCI_SRC_BATERR
|EC_SCI_SRC_BATSOC
|EC_SCI_SRC_BATTERY
))
434 kobject_uevent(&olpc_bat
.dev
->kobj
, KOBJ_CHANGE
);
437 static int __init
olpc_bat_init(void)
442 if (!olpc_platform_info
.ecver
)
446 * We've seen a number of EC protocol changes; this driver requires
447 * the latest EC protocol, supported by 0x44 and above.
449 if (olpc_platform_info
.ecver
< 0x44) {
450 printk(KERN_NOTICE
"OLPC EC version 0x%02x too old for "
451 "battery driver.\n", olpc_platform_info
.ecver
);
455 ret
= olpc_ec_cmd(EC_BAT_STATUS
, NULL
, 0, &status
, 1);
459 /* Ignore the status. It doesn't actually matter */
461 bat_pdev
= platform_device_register_simple("olpc-battery", 0, NULL
, 0);
462 if (IS_ERR(bat_pdev
))
463 return PTR_ERR(bat_pdev
);
465 ret
= power_supply_register(&bat_pdev
->dev
, &olpc_ac
);
469 olpc_bat
.name
= bat_pdev
->name
;
471 ret
= power_supply_register(&bat_pdev
->dev
, &olpc_bat
);
475 ret
= device_create_bin_file(olpc_bat
.dev
, &olpc_bat_eeprom
);
479 ret
= device_create_file(olpc_bat
.dev
, &olpc_bat_error
);
486 device_remove_bin_file(olpc_bat
.dev
, &olpc_bat_eeprom
);
488 power_supply_unregister(&olpc_bat
);
490 power_supply_unregister(&olpc_ac
);
492 platform_device_unregister(bat_pdev
);
497 static void __exit
olpc_bat_exit(void)
499 device_remove_file(olpc_bat
.dev
, &olpc_bat_error
);
500 device_remove_bin_file(olpc_bat
.dev
, &olpc_bat_eeprom
);
501 power_supply_unregister(&olpc_bat
);
502 power_supply_unregister(&olpc_ac
);
503 platform_device_unregister(bat_pdev
);
506 module_init(olpc_bat_init
);
507 module_exit(olpc_bat_exit
);
509 MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
510 MODULE_LICENSE("GPL");
511 MODULE_DESCRIPTION("Battery driver for One Laptop Per Child 'XO' machine");