olpc_battery.c revision d7eb9e36c42504e87c7d92dd5c05cb6f2cf74d28
1fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse/* 2fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse * Battery driver for One Laptop Per Child board. 3fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse * 4fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse * Copyright © 2006 David Woodhouse <dwmw2@infradead.org> 5fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse * 6fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse * This program is free software; you can redistribute it and/or modify 7fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse * it under the terms of the GNU General Public License version 2 as 8fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse * published by the Free Software Foundation. 9fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse */ 10fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse 11fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse#include <linux/module.h> 12fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse#include <linux/err.h> 13fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse#include <linux/platform_device.h> 14fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse#include <linux/power_supply.h> 15fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse#include <linux/jiffies.h> 16fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse#include <linux/sched.h> 17fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse#include <asm/olpc.h> 18fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse 19fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse 20fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse#define EC_BAT_VOLTAGE 0x10 /* uint16_t, *9.76/32, mV */ 21fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse#define EC_BAT_CURRENT 0x11 /* int16_t, *15.625/120, mA */ 22fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse#define EC_BAT_ACR 0x12 23fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse#define EC_BAT_TEMP 0x13 /* uint16_t, *100/256, °C */ 24fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse#define EC_AMB_TEMP 0x14 /* uint16_t, *100/256, °C */ 25fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse#define EC_BAT_STATUS 0x15 /* uint8_t, bitmask */ 26fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse#define EC_BAT_SOC 0x16 /* uint8_t, percentage */ 27fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse#define EC_BAT_SERIAL 0x17 /* uint8_t[6] */ 28fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse#define EC_BAT_EEPROM 0x18 /* uint8_t adr as input, uint8_t output */ 29fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse#define EC_BAT_ERRCODE 0x1f /* uint8_t, bitmask */ 30fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse 31fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse#define BAT_STAT_PRESENT 0x01 32fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse#define BAT_STAT_FULL 0x02 33fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse#define BAT_STAT_LOW 0x04 34fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse#define BAT_STAT_DESTROY 0x08 35fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse#define BAT_STAT_AC 0x10 36fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse#define BAT_STAT_CHARGING 0x20 37fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse#define BAT_STAT_DISCHARGING 0x40 38fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse 39fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse#define BAT_ERR_INFOFAIL 0x02 40fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse#define BAT_ERR_OVERVOLTAGE 0x04 41fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse#define BAT_ERR_OVERTEMP 0x05 42fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse#define BAT_ERR_GAUGESTOP 0x06 43fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse#define BAT_ERR_OUT_OF_CONTROL 0x07 44fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse#define BAT_ERR_ID_FAIL 0x09 45fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse#define BAT_ERR_ACR_FAIL 0x10 46fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse 47fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse#define BAT_ADDR_MFR_TYPE 0x5F 48fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse 49fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse/********************************************************************* 50fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse * Power 51fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse *********************************************************************/ 52fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse 53fb972873a767220333ffb509de8d9131336e212cDavid Woodhousestatic int olpc_ac_get_prop(struct power_supply *psy, 54fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse enum power_supply_property psp, 55fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse union power_supply_propval *val) 56fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse{ 57fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse int ret = 0; 58fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse uint8_t status; 59fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse 60fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse switch (psp) { 61fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse case POWER_SUPPLY_PROP_ONLINE: 62fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse ret = olpc_ec_cmd(EC_BAT_STATUS, NULL, 0, &status, 1); 63fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse if (ret) 64fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse return ret; 65fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse 66fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse val->intval = !!(status & BAT_STAT_AC); 67fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse break; 68fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse default: 69fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse ret = -EINVAL; 70fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse break; 71fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse } 72fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse return ret; 73fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse} 74fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse 75fb972873a767220333ffb509de8d9131336e212cDavid Woodhousestatic enum power_supply_property olpc_ac_props[] = { 76fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse POWER_SUPPLY_PROP_ONLINE, 77fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse}; 78fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse 79fb972873a767220333ffb509de8d9131336e212cDavid Woodhousestatic struct power_supply olpc_ac = { 80fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse .name = "olpc-ac", 81fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse .type = POWER_SUPPLY_TYPE_MAINS, 82fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse .properties = olpc_ac_props, 83fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse .num_properties = ARRAY_SIZE(olpc_ac_props), 84fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse .get_property = olpc_ac_get_prop, 85fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse}; 86fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse 871ca5b9d2183f11bb8b69e04b19a7faf7f600a840David Woodhousestatic char bat_serial[17]; /* Ick */ 881ca5b9d2183f11bb8b69e04b19a7faf7f600a840David Woodhouse 89fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse/********************************************************************* 90fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse * Battery properties 91fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse *********************************************************************/ 92fb972873a767220333ffb509de8d9131336e212cDavid Woodhousestatic int olpc_bat_get_property(struct power_supply *psy, 93fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse enum power_supply_property psp, 94fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse union power_supply_propval *val) 95fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse{ 96fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse int ret = 0; 97fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse int16_t ec_word; 98fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse uint8_t ec_byte; 991ca5b9d2183f11bb8b69e04b19a7faf7f600a840David Woodhouse uint64_t ser_buf; 100fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse 101fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse ret = olpc_ec_cmd(EC_BAT_STATUS, NULL, 0, &ec_byte, 1); 102fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse if (ret) 103fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse return ret; 104fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse 105fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse /* Theoretically there's a race here -- the battery could be 106fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse removed immediately after we check whether it's present, and 107fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse then we query for some other property of the now-absent battery. 108fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse It doesn't matter though -- the EC will return the last-known 109fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse information, and it's as if we just ran that _little_ bit faster 110fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse and managed to read it out before the battery went away. */ 111fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse if (!(ec_byte & BAT_STAT_PRESENT) && psp != POWER_SUPPLY_PROP_PRESENT) 112fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse return -ENODEV; 113fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse 114fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse switch (psp) { 115fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse case POWER_SUPPLY_PROP_STATUS: 116fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse if (olpc_platform_info.ecver > 0x44) { 117fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse if (ec_byte & BAT_STAT_CHARGING) 118fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse val->intval = POWER_SUPPLY_STATUS_CHARGING; 119fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse else if (ec_byte & BAT_STAT_DISCHARGING) 120fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse val->intval = POWER_SUPPLY_STATUS_DISCHARGING; 121fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse else if (ec_byte & BAT_STAT_FULL) 122fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse val->intval = POWER_SUPPLY_STATUS_FULL; 123fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse else /* er,... */ 124fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; 125fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse } else { 126fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse /* Older EC didn't report charge/discharge bits */ 127fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse if (!(ec_byte & BAT_STAT_AC)) /* No AC means discharging */ 128fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse val->intval = POWER_SUPPLY_STATUS_DISCHARGING; 129fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse else if (ec_byte & BAT_STAT_FULL) 130fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse val->intval = POWER_SUPPLY_STATUS_FULL; 131fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse else /* Not _necessarily_ true but EC doesn't tell all yet */ 132fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse val->intval = POWER_SUPPLY_STATUS_CHARGING; 133fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse break; 134fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse } 135fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse case POWER_SUPPLY_PROP_PRESENT: 136fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse val->intval = !!(ec_byte & BAT_STAT_PRESENT); 137fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse break; 138fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse 139fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse case POWER_SUPPLY_PROP_HEALTH: 140fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse if (ec_byte & BAT_STAT_DESTROY) 141fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse val->intval = POWER_SUPPLY_HEALTH_DEAD; 142fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse else { 143fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse ret = olpc_ec_cmd(EC_BAT_ERRCODE, NULL, 0, &ec_byte, 1); 144fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse if (ret) 145fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse return ret; 146fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse 147fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse switch (ec_byte) { 148fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse case 0: 149fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse val->intval = POWER_SUPPLY_HEALTH_GOOD; 150fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse break; 151fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse 152fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse case BAT_ERR_OVERTEMP: 153fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; 154fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse break; 155fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse 156fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse case BAT_ERR_OVERVOLTAGE: 157fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE; 158fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse break; 159fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse 160fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse case BAT_ERR_INFOFAIL: 161fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse case BAT_ERR_OUT_OF_CONTROL: 162fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse case BAT_ERR_ID_FAIL: 163fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse case BAT_ERR_ACR_FAIL: 164fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; 165fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse break; 166fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse 167fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse default: 168fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse /* Eep. We don't know this failure code */ 169fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse return -EIO; 170fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse } 171fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse } 172fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse break; 173fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse 174fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse case POWER_SUPPLY_PROP_MANUFACTURER: 175fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse ec_byte = BAT_ADDR_MFR_TYPE; 176fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse ret = olpc_ec_cmd(EC_BAT_EEPROM, &ec_byte, 1, &ec_byte, 1); 177fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse if (ret) 178fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse return ret; 179fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse 180fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse switch (ec_byte >> 4) { 181fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse case 1: 182fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse val->strval = "Gold Peak"; 183fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse break; 184fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse case 2: 185fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse val->strval = "BYD"; 186fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse break; 187fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse default: 188fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse val->strval = "Unknown"; 189fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse break; 190fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse } 191fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse break; 192fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse case POWER_SUPPLY_PROP_TECHNOLOGY: 193fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse ec_byte = BAT_ADDR_MFR_TYPE; 194fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse ret = olpc_ec_cmd(EC_BAT_EEPROM, &ec_byte, 1, &ec_byte, 1); 195fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse if (ret) 196fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse return ret; 197fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse 198fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse switch (ec_byte & 0xf) { 199fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse case 1: 200fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse val->intval = POWER_SUPPLY_TECHNOLOGY_NiMH; 201fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse break; 202fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse case 2: 203fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse val->intval = POWER_SUPPLY_TECHNOLOGY_LiFe; 204fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse break; 205fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse default: 206fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse val->intval = POWER_SUPPLY_TECHNOLOGY_UNKNOWN; 207fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse break; 208fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse } 209fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse break; 210fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse case POWER_SUPPLY_PROP_VOLTAGE_AVG: 211fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse ret = olpc_ec_cmd(EC_BAT_VOLTAGE, NULL, 0, (void *)&ec_word, 2); 212fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse if (ret) 213fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse return ret; 214fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse 215fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse ec_word = be16_to_cpu(ec_word); 216fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse val->intval = ec_word * 9760L / 32; 217fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse break; 218fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse case POWER_SUPPLY_PROP_CURRENT_AVG: 219fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse ret = olpc_ec_cmd(EC_BAT_CURRENT, NULL, 0, (void *)&ec_word, 2); 220fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse if (ret) 221fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse return ret; 222fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse 223fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse ec_word = be16_to_cpu(ec_word); 224fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse val->intval = ec_word * 15625L / 120; 225fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse break; 226fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse case POWER_SUPPLY_PROP_CAPACITY: 227fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse ret = olpc_ec_cmd(EC_BAT_SOC, NULL, 0, &ec_byte, 1); 228fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse if (ret) 229fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse return ret; 230fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse val->intval = ec_byte; 231fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse break; 232fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse case POWER_SUPPLY_PROP_TEMP: 233fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse ret = olpc_ec_cmd(EC_BAT_TEMP, NULL, 0, (void *)&ec_word, 2); 234fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse if (ret) 235fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse return ret; 236fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse ec_word = be16_to_cpu(ec_word); 237fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse val->intval = ec_word * 100 / 256; 238fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse break; 239fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse case POWER_SUPPLY_PROP_TEMP_AMBIENT: 240fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse ret = olpc_ec_cmd(EC_AMB_TEMP, NULL, 0, (void *)&ec_word, 2); 241fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse if (ret) 242fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse return ret; 243fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse 244fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse ec_word = be16_to_cpu(ec_word); 245fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse val->intval = ec_word * 100 / 256; 246fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse break; 2471ca5b9d2183f11bb8b69e04b19a7faf7f600a840David Woodhouse case POWER_SUPPLY_PROP_SERIAL_NUMBER: 2481ca5b9d2183f11bb8b69e04b19a7faf7f600a840David Woodhouse ret = olpc_ec_cmd(EC_BAT_SERIAL, NULL, 0, (void *)&ser_buf, 8); 2491ca5b9d2183f11bb8b69e04b19a7faf7f600a840David Woodhouse if (ret) 2501ca5b9d2183f11bb8b69e04b19a7faf7f600a840David Woodhouse return ret; 2511ca5b9d2183f11bb8b69e04b19a7faf7f600a840David Woodhouse 2521ca5b9d2183f11bb8b69e04b19a7faf7f600a840David Woodhouse sprintf(bat_serial, "%016llx", (long long)be64_to_cpu(ser_buf)); 2531ca5b9d2183f11bb8b69e04b19a7faf7f600a840David Woodhouse val->strval = bat_serial; 2541ca5b9d2183f11bb8b69e04b19a7faf7f600a840David Woodhouse break; 255fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse default: 256fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse ret = -EINVAL; 257fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse break; 258fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse } 259fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse 260fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse return ret; 261fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse} 262fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse 263fb972873a767220333ffb509de8d9131336e212cDavid Woodhousestatic enum power_supply_property olpc_bat_props[] = { 264fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse POWER_SUPPLY_PROP_STATUS, 265fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse POWER_SUPPLY_PROP_PRESENT, 266fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse POWER_SUPPLY_PROP_HEALTH, 267fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse POWER_SUPPLY_PROP_TECHNOLOGY, 268fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse POWER_SUPPLY_PROP_VOLTAGE_AVG, 269fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse POWER_SUPPLY_PROP_CURRENT_AVG, 270fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse POWER_SUPPLY_PROP_CAPACITY, 271fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse POWER_SUPPLY_PROP_TEMP, 272fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse POWER_SUPPLY_PROP_TEMP_AMBIENT, 273fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse POWER_SUPPLY_PROP_MANUFACTURER, 2741ca5b9d2183f11bb8b69e04b19a7faf7f600a840David Woodhouse POWER_SUPPLY_PROP_SERIAL_NUMBER, 275fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse}; 276fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse 277d7eb9e36c42504e87c7d92dd5c05cb6f2cf74d28Andres Salomon/* EEPROM reading goes completely around the power_supply API, sadly */ 278d7eb9e36c42504e87c7d92dd5c05cb6f2cf74d28Andres Salomon 279d7eb9e36c42504e87c7d92dd5c05cb6f2cf74d28Andres Salomon#define EEPROM_START 0x20 280d7eb9e36c42504e87c7d92dd5c05cb6f2cf74d28Andres Salomon#define EEPROM_END 0x80 281d7eb9e36c42504e87c7d92dd5c05cb6f2cf74d28Andres Salomon#define EEPROM_SIZE (EEPROM_END - EEPROM_START) 282d7eb9e36c42504e87c7d92dd5c05cb6f2cf74d28Andres Salomon 283d7eb9e36c42504e87c7d92dd5c05cb6f2cf74d28Andres Salomonstatic ssize_t olpc_bat_eeprom_read(struct kobject *kobj, 284d7eb9e36c42504e87c7d92dd5c05cb6f2cf74d28Andres Salomon struct bin_attribute *attr, char *buf, loff_t off, size_t count) 285d7eb9e36c42504e87c7d92dd5c05cb6f2cf74d28Andres Salomon{ 286d7eb9e36c42504e87c7d92dd5c05cb6f2cf74d28Andres Salomon uint8_t ec_byte; 287d7eb9e36c42504e87c7d92dd5c05cb6f2cf74d28Andres Salomon int ret, end; 288d7eb9e36c42504e87c7d92dd5c05cb6f2cf74d28Andres Salomon 289d7eb9e36c42504e87c7d92dd5c05cb6f2cf74d28Andres Salomon if (off >= EEPROM_SIZE) 290d7eb9e36c42504e87c7d92dd5c05cb6f2cf74d28Andres Salomon return 0; 291d7eb9e36c42504e87c7d92dd5c05cb6f2cf74d28Andres Salomon if (off + count > EEPROM_SIZE) 292d7eb9e36c42504e87c7d92dd5c05cb6f2cf74d28Andres Salomon count = EEPROM_SIZE - off; 293d7eb9e36c42504e87c7d92dd5c05cb6f2cf74d28Andres Salomon 294d7eb9e36c42504e87c7d92dd5c05cb6f2cf74d28Andres Salomon end = EEPROM_START + off + count; 295d7eb9e36c42504e87c7d92dd5c05cb6f2cf74d28Andres Salomon for (ec_byte = EEPROM_START + off; ec_byte < end; ec_byte++) { 296d7eb9e36c42504e87c7d92dd5c05cb6f2cf74d28Andres Salomon ret = olpc_ec_cmd(EC_BAT_EEPROM, &ec_byte, 1, 297d7eb9e36c42504e87c7d92dd5c05cb6f2cf74d28Andres Salomon &buf[ec_byte - EEPROM_START], 1); 298d7eb9e36c42504e87c7d92dd5c05cb6f2cf74d28Andres Salomon if (ret) { 299d7eb9e36c42504e87c7d92dd5c05cb6f2cf74d28Andres Salomon printk(KERN_ERR "olpc-battery: EC command " 300d7eb9e36c42504e87c7d92dd5c05cb6f2cf74d28Andres Salomon "EC_BAT_EEPROM @ 0x%x failed -" 301d7eb9e36c42504e87c7d92dd5c05cb6f2cf74d28Andres Salomon " %d!\n", ec_byte, ret); 302d7eb9e36c42504e87c7d92dd5c05cb6f2cf74d28Andres Salomon return -EIO; 303d7eb9e36c42504e87c7d92dd5c05cb6f2cf74d28Andres Salomon } 304d7eb9e36c42504e87c7d92dd5c05cb6f2cf74d28Andres Salomon } 305d7eb9e36c42504e87c7d92dd5c05cb6f2cf74d28Andres Salomon 306d7eb9e36c42504e87c7d92dd5c05cb6f2cf74d28Andres Salomon return count; 307d7eb9e36c42504e87c7d92dd5c05cb6f2cf74d28Andres Salomon} 308d7eb9e36c42504e87c7d92dd5c05cb6f2cf74d28Andres Salomon 309d7eb9e36c42504e87c7d92dd5c05cb6f2cf74d28Andres Salomonstatic struct bin_attribute olpc_bat_eeprom = { 310d7eb9e36c42504e87c7d92dd5c05cb6f2cf74d28Andres Salomon .attr = { 311d7eb9e36c42504e87c7d92dd5c05cb6f2cf74d28Andres Salomon .name = "eeprom", 312d7eb9e36c42504e87c7d92dd5c05cb6f2cf74d28Andres Salomon .mode = S_IRUGO, 313d7eb9e36c42504e87c7d92dd5c05cb6f2cf74d28Andres Salomon .owner = THIS_MODULE, 314d7eb9e36c42504e87c7d92dd5c05cb6f2cf74d28Andres Salomon }, 315d7eb9e36c42504e87c7d92dd5c05cb6f2cf74d28Andres Salomon .size = 0, 316d7eb9e36c42504e87c7d92dd5c05cb6f2cf74d28Andres Salomon .read = olpc_bat_eeprom_read, 317d7eb9e36c42504e87c7d92dd5c05cb6f2cf74d28Andres Salomon}; 318d7eb9e36c42504e87c7d92dd5c05cb6f2cf74d28Andres Salomon 319fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse/********************************************************************* 320fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse * Initialisation 321fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse *********************************************************************/ 322fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse 323fb972873a767220333ffb509de8d9131336e212cDavid Woodhousestatic struct platform_device *bat_pdev; 324fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse 325fb972873a767220333ffb509de8d9131336e212cDavid Woodhousestatic struct power_supply olpc_bat = { 326fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse .properties = olpc_bat_props, 327fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse .num_properties = ARRAY_SIZE(olpc_bat_props), 328fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse .get_property = olpc_bat_get_property, 329fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse .use_for_apm = 1, 330fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse}; 331fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse 332fb972873a767220333ffb509de8d9131336e212cDavid Woodhousevoid olpc_battery_trigger_uevent(unsigned long cause) 333fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse{ 334fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse if (cause & EC_SCI_SRC_ACPWR) 335fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse kobject_uevent(&olpc_ac.dev->kobj, KOBJ_CHANGE); 336fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse if (cause & (EC_SCI_SRC_BATERR|EC_SCI_SRC_BATSOC|EC_SCI_SRC_BATTERY)) 337fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse kobject_uevent(&olpc_bat.dev->kobj, KOBJ_CHANGE); 338fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse} 339fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse 340fb972873a767220333ffb509de8d9131336e212cDavid Woodhousestatic int __init olpc_bat_init(void) 341fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse{ 342fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse int ret = 0; 343fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse uint8_t status; 344fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse 345fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse if (!olpc_platform_info.ecver) 346fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse return -ENXIO; 347fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse if (olpc_platform_info.ecver < 0x43) { 348fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse printk(KERN_NOTICE "OLPC EC version 0x%02x too old for battery driver.\n", olpc_platform_info.ecver); 349fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse return -ENXIO; 350fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse } 351fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse 352fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse ret = olpc_ec_cmd(EC_BAT_STATUS, NULL, 0, &status, 1); 353fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse if (ret) 354fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse return ret; 355fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse 356fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse /* Ignore the status. It doesn't actually matter */ 357fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse 358fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse bat_pdev = platform_device_register_simple("olpc-battery", 0, NULL, 0); 359fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse if (IS_ERR(bat_pdev)) 360fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse return PTR_ERR(bat_pdev); 361fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse 362fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse ret = power_supply_register(&bat_pdev->dev, &olpc_ac); 363fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse if (ret) 364fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse goto ac_failed; 365fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse 366fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse olpc_bat.name = bat_pdev->name; 367fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse 368fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse ret = power_supply_register(&bat_pdev->dev, &olpc_bat); 369fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse if (ret) 370fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse goto battery_failed; 371fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse 372d7eb9e36c42504e87c7d92dd5c05cb6f2cf74d28Andres Salomon ret = device_create_bin_file(olpc_bat.dev, &olpc_bat_eeprom); 373d7eb9e36c42504e87c7d92dd5c05cb6f2cf74d28Andres Salomon if (ret) 374d7eb9e36c42504e87c7d92dd5c05cb6f2cf74d28Andres Salomon goto eeprom_failed; 375d7eb9e36c42504e87c7d92dd5c05cb6f2cf74d28Andres Salomon 376fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse goto success; 377fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse 378d7eb9e36c42504e87c7d92dd5c05cb6f2cf74d28Andres Salomoneeprom_failed: 379d7eb9e36c42504e87c7d92dd5c05cb6f2cf74d28Andres Salomon power_supply_unregister(&olpc_bat); 380fb972873a767220333ffb509de8d9131336e212cDavid Woodhousebattery_failed: 381fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse power_supply_unregister(&olpc_ac); 382fb972873a767220333ffb509de8d9131336e212cDavid Woodhouseac_failed: 383fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse platform_device_unregister(bat_pdev); 384fb972873a767220333ffb509de8d9131336e212cDavid Woodhousesuccess: 385fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse return ret; 386fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse} 387fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse 388fb972873a767220333ffb509de8d9131336e212cDavid Woodhousestatic void __exit olpc_bat_exit(void) 389fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse{ 390d7eb9e36c42504e87c7d92dd5c05cb6f2cf74d28Andres Salomon device_remove_bin_file(olpc_bat.dev, &olpc_bat_eeprom); 391fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse power_supply_unregister(&olpc_bat); 392fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse power_supply_unregister(&olpc_ac); 393fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse platform_device_unregister(bat_pdev); 394fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse} 395fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse 396fb972873a767220333ffb509de8d9131336e212cDavid Woodhousemodule_init(olpc_bat_init); 397fb972873a767220333ffb509de8d9131336e212cDavid Woodhousemodule_exit(olpc_bat_exit); 398fb972873a767220333ffb509de8d9131336e212cDavid Woodhouse 399fb972873a767220333ffb509de8d9131336e212cDavid WoodhouseMODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); 400fb972873a767220333ffb509de8d9131336e212cDavid WoodhouseMODULE_LICENSE("GPL"); 401fb972873a767220333ffb509de8d9131336e212cDavid WoodhouseMODULE_DESCRIPTION("Battery driver for One Laptop Per Child 'XO' machine"); 402