olpc_battery.c revision b202a5e6c94d8c43ade5adb97e87734cc4f4f228
1/* 2 * Battery driver for One Laptop Per Child board. 3 * 4 * Copyright © 2006-2010 David Woodhouse <dwmw2@infradead.org> 5 * 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. 9 */ 10 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> 20#include <asm/olpc.h> 21 22 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 */ 33 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 42 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 50 51#define BAT_ADDR_MFR_TYPE 0x5F 52 53/********************************************************************* 54 * Power 55 *********************************************************************/ 56 57static int olpc_ac_get_prop(struct power_supply *psy, 58 enum power_supply_property psp, 59 union power_supply_propval *val) 60{ 61 int ret = 0; 62 uint8_t status; 63 64 switch (psp) { 65 case POWER_SUPPLY_PROP_ONLINE: 66 ret = olpc_ec_cmd(EC_BAT_STATUS, NULL, 0, &status, 1); 67 if (ret) 68 return ret; 69 70 val->intval = !!(status & BAT_STAT_AC); 71 break; 72 default: 73 ret = -EINVAL; 74 break; 75 } 76 return ret; 77} 78 79static enum power_supply_property olpc_ac_props[] = { 80 POWER_SUPPLY_PROP_ONLINE, 81}; 82 83static struct power_supply olpc_ac = { 84 .name = "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, 89}; 90 91static char bat_serial[17]; /* Ick */ 92 93static int olpc_bat_get_status(union power_supply_propval *val, uint8_t ec_byte) 94{ 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; 102 else /* er,... */ 103 val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; 104 } else { 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; 112 } 113 114 return 0; 115} 116 117static int olpc_bat_get_health(union power_supply_propval *val) 118{ 119 uint8_t ec_byte; 120 int ret; 121 122 ret = olpc_ec_cmd(EC_BAT_ERRCODE, NULL, 0, &ec_byte, 1); 123 if (ret) 124 return ret; 125 126 switch (ec_byte) { 127 case 0: 128 val->intval = POWER_SUPPLY_HEALTH_GOOD; 129 break; 130 131 case BAT_ERR_OVERTEMP: 132 val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; 133 break; 134 135 case BAT_ERR_OVERVOLTAGE: 136 val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE; 137 break; 138 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; 144 break; 145 146 default: 147 /* Eep. We don't know this failure code */ 148 ret = -EIO; 149 } 150 151 return ret; 152} 153 154static int olpc_bat_get_mfr(union power_supply_propval *val) 155{ 156 uint8_t ec_byte; 157 int ret; 158 159 ec_byte = BAT_ADDR_MFR_TYPE; 160 ret = olpc_ec_cmd(EC_BAT_EEPROM, &ec_byte, 1, &ec_byte, 1); 161 if (ret) 162 return ret; 163 164 switch (ec_byte >> 4) { 165 case 1: 166 val->strval = "Gold Peak"; 167 break; 168 case 2: 169 val->strval = "BYD"; 170 break; 171 default: 172 val->strval = "Unknown"; 173 break; 174 } 175 176 return ret; 177} 178 179static int olpc_bat_get_tech(union power_supply_propval *val) 180{ 181 uint8_t ec_byte; 182 int ret; 183 184 ec_byte = BAT_ADDR_MFR_TYPE; 185 ret = olpc_ec_cmd(EC_BAT_EEPROM, &ec_byte, 1, &ec_byte, 1); 186 if (ret) 187 return ret; 188 189 switch (ec_byte & 0xf) { 190 case 1: 191 val->intval = POWER_SUPPLY_TECHNOLOGY_NiMH; 192 break; 193 case 2: 194 val->intval = POWER_SUPPLY_TECHNOLOGY_LiFe; 195 break; 196 default: 197 val->intval = POWER_SUPPLY_TECHNOLOGY_UNKNOWN; 198 break; 199 } 200 201 return ret; 202} 203 204static int olpc_bat_get_charge_full_design(union power_supply_propval *val) 205{ 206 uint8_t ec_byte; 207 union power_supply_propval tech; 208 int ret, mfr; 209 210 ret = olpc_bat_get_tech(&tech); 211 if (ret) 212 return ret; 213 214 ec_byte = BAT_ADDR_MFR_TYPE; 215 ret = olpc_ec_cmd(EC_BAT_EEPROM, &ec_byte, 1, &ec_byte, 1); 216 if (ret) 217 return ret; 218 219 mfr = ec_byte >> 4; 220 221 switch (tech.intval) { 222 case POWER_SUPPLY_TECHNOLOGY_NiMH: 223 switch (mfr) { 224 case 1: /* Gold Peak */ 225 val->intval = 3000000*.8; 226 break; 227 default: 228 return -EIO; 229 } 230 break; 231 232 case POWER_SUPPLY_TECHNOLOGY_LiFe: 233 switch (mfr) { 234 case 1: /* Gold Peak */ 235 val->intval = 2800000; 236 break; 237 case 2: /* BYD */ 238 val->intval = 3100000; 239 break; 240 default: 241 return -EIO; 242 } 243 break; 244 245 default: 246 return -EIO; 247 } 248 249 return ret; 250} 251 252/********************************************************************* 253 * Battery properties 254 *********************************************************************/ 255static int olpc_bat_get_property(struct power_supply *psy, 256 enum power_supply_property psp, 257 union power_supply_propval *val) 258{ 259 int ret = 0; 260 __be16 ec_word; 261 uint8_t ec_byte; 262 __be64 ser_buf; 263 264 ret = olpc_ec_cmd(EC_BAT_STATUS, NULL, 0, &ec_byte, 1); 265 if (ret) 266 return ret; 267 268 /* Theoretically there's a race here -- the battery could be 269 removed immediately after we check whether it's present, and 270 then we query for some other property of the now-absent battery. 271 It doesn't matter though -- the EC will return the last-known 272 information, and it's as if we just ran that _little_ bit faster 273 and managed to read it out before the battery went away. */ 274 if (!(ec_byte & (BAT_STAT_PRESENT | BAT_STAT_TRICKLE)) && 275 psp != POWER_SUPPLY_PROP_PRESENT) 276 return -ENODEV; 277 278 switch (psp) { 279 case POWER_SUPPLY_PROP_STATUS: 280 ret = olpc_bat_get_status(val, ec_byte); 281 if (ret) 282 return ret; 283 break; 284 case POWER_SUPPLY_PROP_CHARGE_TYPE: 285 if (ec_byte & BAT_STAT_TRICKLE) 286 val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; 287 else if (ec_byte & BAT_STAT_CHARGING) 288 val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST; 289 else 290 val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE; 291 break; 292 case POWER_SUPPLY_PROP_PRESENT: 293 val->intval = !!(ec_byte & (BAT_STAT_PRESENT | 294 BAT_STAT_TRICKLE)); 295 break; 296 297 case POWER_SUPPLY_PROP_HEALTH: 298 if (ec_byte & BAT_STAT_DESTROY) 299 val->intval = POWER_SUPPLY_HEALTH_DEAD; 300 else { 301 ret = olpc_bat_get_health(val); 302 if (ret) 303 return ret; 304 } 305 break; 306 307 case POWER_SUPPLY_PROP_MANUFACTURER: 308 ret = olpc_bat_get_mfr(val); 309 if (ret) 310 return ret; 311 break; 312 case POWER_SUPPLY_PROP_TECHNOLOGY: 313 ret = olpc_bat_get_tech(val); 314 if (ret) 315 return ret; 316 break; 317 case POWER_SUPPLY_PROP_VOLTAGE_AVG: 318 ret = olpc_ec_cmd(EC_BAT_VOLTAGE, NULL, 0, (void *)&ec_word, 2); 319 if (ret) 320 return ret; 321 322 val->intval = (s16)be16_to_cpu(ec_word) * 9760L / 32; 323 break; 324 case POWER_SUPPLY_PROP_CURRENT_AVG: 325 ret = olpc_ec_cmd(EC_BAT_CURRENT, NULL, 0, (void *)&ec_word, 2); 326 if (ret) 327 return ret; 328 329 val->intval = (s16)be16_to_cpu(ec_word) * 15625L / 120; 330 break; 331 case POWER_SUPPLY_PROP_CAPACITY: 332 ret = olpc_ec_cmd(EC_BAT_SOC, NULL, 0, &ec_byte, 1); 333 if (ret) 334 return ret; 335 val->intval = ec_byte; 336 break; 337 case POWER_SUPPLY_PROP_CAPACITY_LEVEL: 338 if (ec_byte & BAT_STAT_FULL) 339 val->intval = POWER_SUPPLY_CAPACITY_LEVEL_FULL; 340 else if (ec_byte & BAT_STAT_LOW) 341 val->intval = POWER_SUPPLY_CAPACITY_LEVEL_LOW; 342 else 343 val->intval = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL; 344 break; 345 case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: 346 ret = olpc_bat_get_charge_full_design(val); 347 if (ret) 348 return ret; 349 break; 350 case POWER_SUPPLY_PROP_TEMP: 351 ret = olpc_ec_cmd(EC_BAT_TEMP, NULL, 0, (void *)&ec_word, 2); 352 if (ret) 353 return ret; 354 355 val->intval = (s16)be16_to_cpu(ec_word) * 100 / 256; 356 break; 357 case POWER_SUPPLY_PROP_TEMP_AMBIENT: 358 ret = olpc_ec_cmd(EC_AMB_TEMP, NULL, 0, (void *)&ec_word, 2); 359 if (ret) 360 return ret; 361 362 val->intval = (int)be16_to_cpu(ec_word) * 100 / 256; 363 break; 364 case POWER_SUPPLY_PROP_CHARGE_COUNTER: 365 ret = olpc_ec_cmd(EC_BAT_ACR, NULL, 0, (void *)&ec_word, 2); 366 if (ret) 367 return ret; 368 369 val->intval = (s16)be16_to_cpu(ec_word) * 6250 / 15; 370 break; 371 case POWER_SUPPLY_PROP_SERIAL_NUMBER: 372 ret = olpc_ec_cmd(EC_BAT_SERIAL, NULL, 0, (void *)&ser_buf, 8); 373 if (ret) 374 return ret; 375 376 sprintf(bat_serial, "%016llx", (long long)be64_to_cpu(ser_buf)); 377 val->strval = bat_serial; 378 break; 379 default: 380 ret = -EINVAL; 381 break; 382 } 383 384 return ret; 385} 386 387static enum power_supply_property olpc_xo1_bat_props[] = { 388 POWER_SUPPLY_PROP_STATUS, 389 POWER_SUPPLY_PROP_CHARGE_TYPE, 390 POWER_SUPPLY_PROP_PRESENT, 391 POWER_SUPPLY_PROP_HEALTH, 392 POWER_SUPPLY_PROP_TECHNOLOGY, 393 POWER_SUPPLY_PROP_VOLTAGE_AVG, 394 POWER_SUPPLY_PROP_CURRENT_AVG, 395 POWER_SUPPLY_PROP_CAPACITY, 396 POWER_SUPPLY_PROP_CAPACITY_LEVEL, 397 POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, 398 POWER_SUPPLY_PROP_TEMP, 399 POWER_SUPPLY_PROP_TEMP_AMBIENT, 400 POWER_SUPPLY_PROP_MANUFACTURER, 401 POWER_SUPPLY_PROP_SERIAL_NUMBER, 402 POWER_SUPPLY_PROP_CHARGE_COUNTER, 403}; 404 405/* XO-1.5 does not have ambient temperature property */ 406static enum power_supply_property olpc_xo15_bat_props[] = { 407 POWER_SUPPLY_PROP_STATUS, 408 POWER_SUPPLY_PROP_CHARGE_TYPE, 409 POWER_SUPPLY_PROP_PRESENT, 410 POWER_SUPPLY_PROP_HEALTH, 411 POWER_SUPPLY_PROP_TECHNOLOGY, 412 POWER_SUPPLY_PROP_VOLTAGE_AVG, 413 POWER_SUPPLY_PROP_CURRENT_AVG, 414 POWER_SUPPLY_PROP_CAPACITY, 415 POWER_SUPPLY_PROP_CAPACITY_LEVEL, 416 POWER_SUPPLY_PROP_TEMP, 417 POWER_SUPPLY_PROP_MANUFACTURER, 418 POWER_SUPPLY_PROP_SERIAL_NUMBER, 419 POWER_SUPPLY_PROP_CHARGE_COUNTER, 420}; 421 422/* EEPROM reading goes completely around the power_supply API, sadly */ 423 424#define EEPROM_START 0x20 425#define EEPROM_END 0x80 426#define EEPROM_SIZE (EEPROM_END - EEPROM_START) 427 428static ssize_t olpc_bat_eeprom_read(struct file *filp, struct kobject *kobj, 429 struct bin_attribute *attr, char *buf, loff_t off, size_t count) 430{ 431 uint8_t ec_byte; 432 int ret; 433 int i; 434 435 if (off >= EEPROM_SIZE) 436 return 0; 437 if (off + count > EEPROM_SIZE) 438 count = EEPROM_SIZE - off; 439 440 for (i = 0; i < count; i++) { 441 ec_byte = EEPROM_START + off + i; 442 ret = olpc_ec_cmd(EC_BAT_EEPROM, &ec_byte, 1, &buf[i], 1); 443 if (ret) { 444 pr_err("olpc-battery: " 445 "EC_BAT_EEPROM cmd @ 0x%x failed - %d!\n", 446 ec_byte, ret); 447 return -EIO; 448 } 449 } 450 451 return count; 452} 453 454static struct bin_attribute olpc_bat_eeprom = { 455 .attr = { 456 .name = "eeprom", 457 .mode = S_IRUGO, 458 }, 459 .size = 0, 460 .read = olpc_bat_eeprom_read, 461}; 462 463/* Allow userspace to see the specific error value pulled from the EC */ 464 465static ssize_t olpc_bat_error_read(struct device *dev, 466 struct device_attribute *attr, char *buf) 467{ 468 uint8_t ec_byte; 469 ssize_t ret; 470 471 ret = olpc_ec_cmd(EC_BAT_ERRCODE, NULL, 0, &ec_byte, 1); 472 if (ret < 0) 473 return ret; 474 475 return sprintf(buf, "%d\n", ec_byte); 476} 477 478static struct device_attribute olpc_bat_error = { 479 .attr = { 480 .name = "error", 481 .mode = S_IRUGO, 482 }, 483 .show = olpc_bat_error_read, 484}; 485 486/********************************************************************* 487 * Initialisation 488 *********************************************************************/ 489 490static struct platform_device *bat_pdev; 491 492static struct power_supply olpc_bat = { 493 .get_property = olpc_bat_get_property, 494 .use_for_apm = 1, 495}; 496 497void olpc_battery_trigger_uevent(unsigned long cause) 498{ 499 if (cause & EC_SCI_SRC_ACPWR) 500 kobject_uevent(&olpc_ac.dev->kobj, KOBJ_CHANGE); 501 if (cause & (EC_SCI_SRC_BATERR|EC_SCI_SRC_BATSOC|EC_SCI_SRC_BATTERY)) 502 kobject_uevent(&olpc_bat.dev->kobj, KOBJ_CHANGE); 503} 504 505static int __init olpc_bat_init(void) 506{ 507 int ret = 0; 508 uint8_t status; 509 510 if (!olpc_platform_info.ecver) 511 return -ENXIO; 512 513 /* 514 * We've seen a number of EC protocol changes; this driver requires 515 * the latest EC protocol, supported by 0x44 and above. 516 */ 517 if (olpc_platform_info.ecver < 0x44) { 518 printk(KERN_NOTICE "OLPC EC version 0x%02x too old for " 519 "battery driver.\n", olpc_platform_info.ecver); 520 return -ENXIO; 521 } 522 523 ret = olpc_ec_cmd(EC_BAT_STATUS, NULL, 0, &status, 1); 524 if (ret) 525 return ret; 526 527 /* Ignore the status. It doesn't actually matter */ 528 529 bat_pdev = platform_device_register_simple("olpc-battery", 0, NULL, 0); 530 if (IS_ERR(bat_pdev)) 531 return PTR_ERR(bat_pdev); 532 533 ret = power_supply_register(&bat_pdev->dev, &olpc_ac); 534 if (ret) 535 goto ac_failed; 536 537 olpc_bat.name = bat_pdev->name; 538 if (olpc_board_at_least(olpc_board_pre(0xd0))) { /* XO-1.5 */ 539 olpc_bat.properties = olpc_xo15_bat_props; 540 olpc_bat.num_properties = ARRAY_SIZE(olpc_xo15_bat_props); 541 } else { /* XO-1 */ 542 olpc_bat.properties = olpc_xo1_bat_props; 543 olpc_bat.num_properties = ARRAY_SIZE(olpc_xo1_bat_props); 544 } 545 546 ret = power_supply_register(&bat_pdev->dev, &olpc_bat); 547 if (ret) 548 goto battery_failed; 549 550 ret = device_create_bin_file(olpc_bat.dev, &olpc_bat_eeprom); 551 if (ret) 552 goto eeprom_failed; 553 554 ret = device_create_file(olpc_bat.dev, &olpc_bat_error); 555 if (ret) 556 goto error_failed; 557 558 goto success; 559 560error_failed: 561 device_remove_bin_file(olpc_bat.dev, &olpc_bat_eeprom); 562eeprom_failed: 563 power_supply_unregister(&olpc_bat); 564battery_failed: 565 power_supply_unregister(&olpc_ac); 566ac_failed: 567 platform_device_unregister(bat_pdev); 568success: 569 return ret; 570} 571 572static void __exit olpc_bat_exit(void) 573{ 574 device_remove_file(olpc_bat.dev, &olpc_bat_error); 575 device_remove_bin_file(olpc_bat.dev, &olpc_bat_eeprom); 576 power_supply_unregister(&olpc_bat); 577 power_supply_unregister(&olpc_ac); 578 platform_device_unregister(bat_pdev); 579} 580 581module_init(olpc_bat_init); 582module_exit(olpc_bat_exit); 583 584MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); 585MODULE_LICENSE("GPL"); 586MODULE_DESCRIPTION("Battery driver for One Laptop Per Child 'XO' machine"); 587