ds2782_battery.c revision 5a0e3ad6af8660be21ca98a971cd00f331318c05
1/* 2 * I2C client/driver for the Maxim/Dallas DS2782 Stand-Alone Fuel Gauge IC 3 * 4 * Copyright (C) 2009 Bluewater Systems Ltd 5 * 6 * Author: Ryan Mallon <ryan@bluewatersys.com> 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License version 2 as 10 * published by the Free Software Foundation. 11 * 12 */ 13 14#include <linux/kernel.h> 15#include <linux/module.h> 16#include <linux/types.h> 17#include <linux/errno.h> 18#include <linux/swab.h> 19#include <linux/i2c.h> 20#include <linux/idr.h> 21#include <linux/power_supply.h> 22#include <linux/slab.h> 23 24#define DS2782_REG_RARC 0x06 /* Remaining active relative capacity */ 25 26#define DS2782_REG_VOLT_MSB 0x0c 27#define DS2782_REG_TEMP_MSB 0x0a 28#define DS2782_REG_CURRENT_MSB 0x0e 29 30/* EEPROM Block */ 31#define DS2782_REG_RSNSP 0x69 /* Sense resistor value */ 32 33/* Current unit measurement in uA for a 1 milli-ohm sense resistor */ 34#define DS2782_CURRENT_UNITS 1563 35 36#define to_ds2782_info(x) container_of(x, struct ds2782_info, battery) 37 38struct ds2782_info { 39 struct i2c_client *client; 40 struct power_supply battery; 41 int id; 42}; 43 44static DEFINE_IDR(battery_id); 45static DEFINE_MUTEX(battery_lock); 46 47static inline int ds2782_read_reg(struct ds2782_info *info, int reg, u8 *val) 48{ 49 int ret; 50 51 ret = i2c_smbus_read_byte_data(info->client, reg); 52 if (ret < 0) { 53 dev_err(&info->client->dev, "register read failed\n"); 54 return ret; 55 } 56 57 *val = ret; 58 return 0; 59} 60 61static inline int ds2782_read_reg16(struct ds2782_info *info, int reg_msb, 62 s16 *val) 63{ 64 int ret; 65 66 ret = swab16(i2c_smbus_read_word_data(info->client, reg_msb)); 67 if (ret < 0) { 68 dev_err(&info->client->dev, "register read failed\n"); 69 return ret; 70 } 71 72 *val = ret; 73 return 0; 74} 75 76static int ds2782_get_temp(struct ds2782_info *info, int *temp) 77{ 78 s16 raw; 79 int err; 80 81 /* 82 * Temperature is measured in units of 0.125 degrees celcius, the 83 * power_supply class measures temperature in tenths of degrees 84 * celsius. The temperature value is stored as a 10 bit number, plus 85 * sign in the upper bits of a 16 bit register. 86 */ 87 err = ds2782_read_reg16(info, DS2782_REG_TEMP_MSB, &raw); 88 if (err) 89 return err; 90 *temp = ((raw / 32) * 125) / 100; 91 return 0; 92} 93 94static int ds2782_get_current(struct ds2782_info *info, int *current_uA) 95{ 96 int sense_res; 97 int err; 98 u8 sense_res_raw; 99 s16 raw; 100 101 /* 102 * The units of measurement for current are dependent on the value of 103 * the sense resistor. 104 */ 105 err = ds2782_read_reg(info, DS2782_REG_RSNSP, &sense_res_raw); 106 if (err) 107 return err; 108 if (sense_res_raw == 0) { 109 dev_err(&info->client->dev, "sense resistor value is 0\n"); 110 return -ENXIO; 111 } 112 sense_res = 1000 / sense_res_raw; 113 114 dev_dbg(&info->client->dev, "sense resistor = %d milli-ohms\n", 115 sense_res); 116 err = ds2782_read_reg16(info, DS2782_REG_CURRENT_MSB, &raw); 117 if (err) 118 return err; 119 *current_uA = raw * (DS2782_CURRENT_UNITS / sense_res); 120 return 0; 121} 122 123static int ds2782_get_voltage(struct ds2782_info *info, int *voltage_uA) 124{ 125 s16 raw; 126 int err; 127 128 /* 129 * Voltage is measured in units of 4.88mV. The voltage is stored as 130 * a 10-bit number plus sign, in the upper bits of a 16-bit register 131 */ 132 err = ds2782_read_reg16(info, DS2782_REG_VOLT_MSB, &raw); 133 if (err) 134 return err; 135 *voltage_uA = (raw / 32) * 4800; 136 return 0; 137} 138 139static int ds2782_get_capacity(struct ds2782_info *info, int *capacity) 140{ 141 int err; 142 u8 raw; 143 144 err = ds2782_read_reg(info, DS2782_REG_RARC, &raw); 145 if (err) 146 return err; 147 *capacity = raw; 148 return raw; 149} 150 151static int ds2782_get_status(struct ds2782_info *info, int *status) 152{ 153 int err; 154 int current_uA; 155 int capacity; 156 157 err = ds2782_get_current(info, ¤t_uA); 158 if (err) 159 return err; 160 161 err = ds2782_get_capacity(info, &capacity); 162 if (err) 163 return err; 164 165 if (capacity == 100) 166 *status = POWER_SUPPLY_STATUS_FULL; 167 else if (current_uA == 0) 168 *status = POWER_SUPPLY_STATUS_NOT_CHARGING; 169 else if (current_uA < 0) 170 *status = POWER_SUPPLY_STATUS_DISCHARGING; 171 else 172 *status = POWER_SUPPLY_STATUS_CHARGING; 173 174 return 0; 175} 176 177static int ds2782_battery_get_property(struct power_supply *psy, 178 enum power_supply_property prop, 179 union power_supply_propval *val) 180{ 181 struct ds2782_info *info = to_ds2782_info(psy); 182 int ret; 183 184 switch (prop) { 185 case POWER_SUPPLY_PROP_STATUS: 186 ret = ds2782_get_status(info, &val->intval); 187 break; 188 189 case POWER_SUPPLY_PROP_CAPACITY: 190 ret = ds2782_get_capacity(info, &val->intval); 191 break; 192 193 case POWER_SUPPLY_PROP_VOLTAGE_NOW: 194 ret = ds2782_get_voltage(info, &val->intval); 195 break; 196 197 case POWER_SUPPLY_PROP_CURRENT_NOW: 198 ret = ds2782_get_current(info, &val->intval); 199 break; 200 201 case POWER_SUPPLY_PROP_TEMP: 202 ret = ds2782_get_temp(info, &val->intval); 203 break; 204 205 default: 206 ret = -EINVAL; 207 } 208 209 return ret; 210} 211 212static enum power_supply_property ds2782_battery_props[] = { 213 POWER_SUPPLY_PROP_STATUS, 214 POWER_SUPPLY_PROP_CAPACITY, 215 POWER_SUPPLY_PROP_VOLTAGE_NOW, 216 POWER_SUPPLY_PROP_CURRENT_NOW, 217 POWER_SUPPLY_PROP_TEMP, 218}; 219 220static void ds2782_power_supply_init(struct power_supply *battery) 221{ 222 battery->type = POWER_SUPPLY_TYPE_BATTERY; 223 battery->properties = ds2782_battery_props; 224 battery->num_properties = ARRAY_SIZE(ds2782_battery_props); 225 battery->get_property = ds2782_battery_get_property; 226 battery->external_power_changed = NULL; 227} 228 229static int ds2782_battery_remove(struct i2c_client *client) 230{ 231 struct ds2782_info *info = i2c_get_clientdata(client); 232 233 power_supply_unregister(&info->battery); 234 kfree(info->battery.name); 235 236 mutex_lock(&battery_lock); 237 idr_remove(&battery_id, info->id); 238 mutex_unlock(&battery_lock); 239 240 i2c_set_clientdata(client, info); 241 242 kfree(info); 243 return 0; 244} 245 246static int ds2782_battery_probe(struct i2c_client *client, 247 const struct i2c_device_id *id) 248{ 249 struct ds2782_info *info; 250 int ret; 251 int num; 252 253 /* Get an ID for this battery */ 254 ret = idr_pre_get(&battery_id, GFP_KERNEL); 255 if (ret == 0) { 256 ret = -ENOMEM; 257 goto fail_id; 258 } 259 260 mutex_lock(&battery_lock); 261 ret = idr_get_new(&battery_id, client, &num); 262 mutex_unlock(&battery_lock); 263 if (ret < 0) 264 goto fail_id; 265 266 info = kzalloc(sizeof(*info), GFP_KERNEL); 267 if (!info) { 268 ret = -ENOMEM; 269 goto fail_info; 270 } 271 272 info->battery.name = kasprintf(GFP_KERNEL, "ds2782-%d", num); 273 if (!info->battery.name) { 274 ret = -ENOMEM; 275 goto fail_name; 276 } 277 278 i2c_set_clientdata(client, info); 279 info->client = client; 280 ds2782_power_supply_init(&info->battery); 281 282 ret = power_supply_register(&client->dev, &info->battery); 283 if (ret) { 284 dev_err(&client->dev, "failed to register battery\n"); 285 goto fail_register; 286 } 287 288 return 0; 289 290fail_register: 291 kfree(info->battery.name); 292fail_name: 293 i2c_set_clientdata(client, info); 294 kfree(info); 295fail_info: 296 mutex_lock(&battery_lock); 297 idr_remove(&battery_id, num); 298 mutex_unlock(&battery_lock); 299fail_id: 300 return ret; 301} 302 303static const struct i2c_device_id ds2782_id[] = { 304 {"ds2782", 0}, 305 {}, 306}; 307 308static struct i2c_driver ds2782_battery_driver = { 309 .driver = { 310 .name = "ds2782-battery", 311 }, 312 .probe = ds2782_battery_probe, 313 .remove = ds2782_battery_remove, 314 .id_table = ds2782_id, 315}; 316 317static int __init ds2782_init(void) 318{ 319 return i2c_add_driver(&ds2782_battery_driver); 320} 321module_init(ds2782_init); 322 323static void __exit ds2782_exit(void) 324{ 325 i2c_del_driver(&ds2782_battery_driver); 326} 327module_exit(ds2782_exit); 328 329MODULE_AUTHOR("Ryan Mallon <ryan@bluewatersys.com>"); 330MODULE_DESCRIPTION("Maxim/Dallas DS2782 Stand-Alone Fuel Gauage IC driver"); 331MODULE_LICENSE("GPL"); 332