ds2782_battery.c revision 9b9ade6b612e562c4a5bd02ef38cc32e10f3f9ba
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 * DS2786 added by Yulia Vilensky <vilensky@compulab.co.il> 9 * 10 * This program is free software; you can redistribute it and/or modify 11 * it under the terms of the GNU General Public License version 2 as 12 * published by the Free Software Foundation. 13 * 14 */ 15 16#include <linux/kernel.h> 17#include <linux/module.h> 18#include <linux/types.h> 19#include <linux/errno.h> 20#include <linux/swab.h> 21#include <linux/i2c.h> 22#include <linux/idr.h> 23#include <linux/power_supply.h> 24#include <linux/slab.h> 25#include <linux/ds2782_battery.h> 26 27#define DS2782_REG_RARC 0x06 /* Remaining active relative capacity */ 28 29#define DS278x_REG_VOLT_MSB 0x0c 30#define DS278x_REG_TEMP_MSB 0x0a 31#define DS278x_REG_CURRENT_MSB 0x0e 32 33/* EEPROM Block */ 34#define DS2782_REG_RSNSP 0x69 /* Sense resistor value */ 35 36/* Current unit measurement in uA for a 1 milli-ohm sense resistor */ 37#define DS2782_CURRENT_UNITS 1563 38 39#define DS2786_REG_RARC 0x02 /* Remaining active relative capacity */ 40 41#define DS2786_CURRENT_UNITS 25 42 43struct ds278x_info; 44 45struct ds278x_battery_ops { 46 int (*get_current)(struct ds278x_info *info, int *current_uA); 47 int (*get_voltage)(struct ds278x_info *info, int *voltage_uA); 48 int (*get_capacity)(struct ds278x_info *info, int *capacity_uA); 49 50}; 51 52#define to_ds278x_info(x) container_of(x, struct ds278x_info, battery) 53 54struct ds278x_info { 55 struct i2c_client *client; 56 struct power_supply battery; 57 struct ds278x_battery_ops *ops; 58 int id; 59 int rsns; 60}; 61 62static DEFINE_IDR(battery_id); 63static DEFINE_MUTEX(battery_lock); 64 65static inline int ds278x_read_reg(struct ds278x_info *info, int reg, u8 *val) 66{ 67 int ret; 68 69 ret = i2c_smbus_read_byte_data(info->client, reg); 70 if (ret < 0) { 71 dev_err(&info->client->dev, "register read failed\n"); 72 return ret; 73 } 74 75 *val = ret; 76 return 0; 77} 78 79static inline int ds278x_read_reg16(struct ds278x_info *info, int reg_msb, 80 s16 *val) 81{ 82 int ret; 83 84 ret = swab16(i2c_smbus_read_word_data(info->client, reg_msb)); 85 if (ret < 0) { 86 dev_err(&info->client->dev, "register read failed\n"); 87 return ret; 88 } 89 90 *val = ret; 91 return 0; 92} 93 94static int ds278x_get_temp(struct ds278x_info *info, int *temp) 95{ 96 s16 raw; 97 int err; 98 99 /* 100 * Temperature is measured in units of 0.125 degrees celcius, the 101 * power_supply class measures temperature in tenths of degrees 102 * celsius. The temperature value is stored as a 10 bit number, plus 103 * sign in the upper bits of a 16 bit register. 104 */ 105 err = ds278x_read_reg16(info, DS278x_REG_TEMP_MSB, &raw); 106 if (err) 107 return err; 108 *temp = ((raw / 32) * 125) / 100; 109 return 0; 110} 111 112static int ds2782_get_current(struct ds278x_info *info, int *current_uA) 113{ 114 int sense_res; 115 int err; 116 u8 sense_res_raw; 117 s16 raw; 118 119 /* 120 * The units of measurement for current are dependent on the value of 121 * the sense resistor. 122 */ 123 err = ds278x_read_reg(info, DS2782_REG_RSNSP, &sense_res_raw); 124 if (err) 125 return err; 126 if (sense_res_raw == 0) { 127 dev_err(&info->client->dev, "sense resistor value is 0\n"); 128 return -ENXIO; 129 } 130 sense_res = 1000 / sense_res_raw; 131 132 dev_dbg(&info->client->dev, "sense resistor = %d milli-ohms\n", 133 sense_res); 134 err = ds278x_read_reg16(info, DS278x_REG_CURRENT_MSB, &raw); 135 if (err) 136 return err; 137 *current_uA = raw * (DS2782_CURRENT_UNITS / sense_res); 138 return 0; 139} 140 141static int ds2782_get_voltage(struct ds278x_info *info, int *voltage_uA) 142{ 143 s16 raw; 144 int err; 145 146 /* 147 * Voltage is measured in units of 4.88mV. The voltage is stored as 148 * a 10-bit number plus sign, in the upper bits of a 16-bit register 149 */ 150 err = ds278x_read_reg16(info, DS278x_REG_VOLT_MSB, &raw); 151 if (err) 152 return err; 153 *voltage_uA = (raw / 32) * 4800; 154 return 0; 155} 156 157static int ds2782_get_capacity(struct ds278x_info *info, int *capacity) 158{ 159 int err; 160 u8 raw; 161 162 err = ds278x_read_reg(info, DS2782_REG_RARC, &raw); 163 if (err) 164 return err; 165 *capacity = raw; 166 return raw; 167} 168 169static int ds2786_get_current(struct ds278x_info *info, int *current_uA) 170{ 171 int err; 172 s16 raw; 173 174 err = ds278x_read_reg16(info, DS278x_REG_CURRENT_MSB, &raw); 175 if (err) 176 return err; 177 *current_uA = (raw / 16) * (DS2786_CURRENT_UNITS / info->rsns); 178 return 0; 179} 180 181static int ds2786_get_voltage(struct ds278x_info *info, int *voltage_uA) 182{ 183 s16 raw; 184 int err; 185 186 /* 187 * Voltage is measured in units of 1.22mV. The voltage is stored as 188 * a 10-bit number plus sign, in the upper bits of a 16-bit register 189 */ 190 err = ds278x_read_reg16(info, DS278x_REG_VOLT_MSB, &raw); 191 if (err) 192 return err; 193 *voltage_uA = (raw / 8) * 1220; 194 return 0; 195} 196 197static int ds2786_get_capacity(struct ds278x_info *info, int *capacity) 198{ 199 int err; 200 u8 raw; 201 202 err = ds278x_read_reg(info, DS2786_REG_RARC, &raw); 203 if (err) 204 return err; 205 /* Relative capacity is displayed with resolution 0.5 % */ 206 *capacity = raw/2 ; 207 return 0; 208} 209 210static int ds278x_get_status(struct ds278x_info *info, int *status) 211{ 212 int err; 213 int current_uA; 214 int capacity; 215 216 err = info->ops->get_current(info, ¤t_uA); 217 if (err) 218 return err; 219 220 err = info->ops->get_capacity(info, &capacity); 221 if (err) 222 return err; 223 224 if (capacity == 100) 225 *status = POWER_SUPPLY_STATUS_FULL; 226 else if (current_uA == 0) 227 *status = POWER_SUPPLY_STATUS_NOT_CHARGING; 228 else if (current_uA < 0) 229 *status = POWER_SUPPLY_STATUS_DISCHARGING; 230 else 231 *status = POWER_SUPPLY_STATUS_CHARGING; 232 233 return 0; 234} 235 236static int ds278x_battery_get_property(struct power_supply *psy, 237 enum power_supply_property prop, 238 union power_supply_propval *val) 239{ 240 struct ds278x_info *info = to_ds278x_info(psy); 241 int ret; 242 243 switch (prop) { 244 case POWER_SUPPLY_PROP_STATUS: 245 ret = ds278x_get_status(info, &val->intval); 246 break; 247 248 case POWER_SUPPLY_PROP_CAPACITY: 249 ret = info->ops->get_capacity(info, &val->intval); 250 break; 251 252 case POWER_SUPPLY_PROP_VOLTAGE_NOW: 253 ret = info->ops->get_voltage(info, &val->intval); 254 break; 255 256 case POWER_SUPPLY_PROP_CURRENT_NOW: 257 ret = info->ops->get_current(info, &val->intval); 258 break; 259 260 case POWER_SUPPLY_PROP_TEMP: 261 ret = ds278x_get_temp(info, &val->intval); 262 break; 263 264 default: 265 ret = -EINVAL; 266 } 267 268 return ret; 269} 270 271static enum power_supply_property ds278x_battery_props[] = { 272 POWER_SUPPLY_PROP_STATUS, 273 POWER_SUPPLY_PROP_CAPACITY, 274 POWER_SUPPLY_PROP_VOLTAGE_NOW, 275 POWER_SUPPLY_PROP_CURRENT_NOW, 276 POWER_SUPPLY_PROP_TEMP, 277}; 278 279static void ds278x_power_supply_init(struct power_supply *battery) 280{ 281 battery->type = POWER_SUPPLY_TYPE_BATTERY; 282 battery->properties = ds278x_battery_props; 283 battery->num_properties = ARRAY_SIZE(ds278x_battery_props); 284 battery->get_property = ds278x_battery_get_property; 285 battery->external_power_changed = NULL; 286} 287 288static int ds278x_battery_remove(struct i2c_client *client) 289{ 290 struct ds278x_info *info = i2c_get_clientdata(client); 291 292 power_supply_unregister(&info->battery); 293 kfree(info->battery.name); 294 295 mutex_lock(&battery_lock); 296 idr_remove(&battery_id, info->id); 297 mutex_unlock(&battery_lock); 298 299 kfree(info); 300 return 0; 301} 302 303static struct ds278x_battery_ops ds278x_ops[] = { 304 [0] = { 305 .get_current = ds2782_get_current, 306 .get_voltage = ds2782_get_voltage, 307 .get_capacity = ds2782_get_capacity, 308 }, 309 [1] = { 310 .get_current = ds2786_get_current, 311 .get_voltage = ds2786_get_voltage, 312 .get_capacity = ds2786_get_capacity, 313 } 314}; 315 316static int ds278x_battery_probe(struct i2c_client *client, 317 const struct i2c_device_id *id) 318{ 319 struct ds278x_platform_data *pdata = client->dev.platform_data; 320 struct ds278x_info *info; 321 int ret; 322 int num; 323 324 /* 325 * ds2786 should have the sense resistor value set 326 * in the platform data 327 */ 328 if (id->driver_data == 1 && !pdata) { 329 dev_err(&client->dev, "missing platform data for ds2786\n"); 330 return -EINVAL; 331 } 332 333 /* Get an ID for this battery */ 334 ret = idr_pre_get(&battery_id, GFP_KERNEL); 335 if (ret == 0) { 336 ret = -ENOMEM; 337 goto fail_id; 338 } 339 340 mutex_lock(&battery_lock); 341 ret = idr_get_new(&battery_id, client, &num); 342 mutex_unlock(&battery_lock); 343 if (ret < 0) 344 goto fail_id; 345 346 info = kzalloc(sizeof(*info), GFP_KERNEL); 347 if (!info) { 348 ret = -ENOMEM; 349 goto fail_info; 350 } 351 352 info->battery.name = kasprintf(GFP_KERNEL, "%s-%d", client->name, num); 353 if (!info->battery.name) { 354 ret = -ENOMEM; 355 goto fail_name; 356 } 357 358 if (id->driver_data == 1) 359 info->rsns = pdata->rsns; 360 361 i2c_set_clientdata(client, info); 362 info->client = client; 363 info->id = num; 364 info->ops = &ds278x_ops[id->driver_data]; 365 ds278x_power_supply_init(&info->battery); 366 367 ret = power_supply_register(&client->dev, &info->battery); 368 if (ret) { 369 dev_err(&client->dev, "failed to register battery\n"); 370 goto fail_register; 371 } 372 373 return 0; 374 375fail_register: 376 kfree(info->battery.name); 377fail_name: 378 kfree(info); 379fail_info: 380 mutex_lock(&battery_lock); 381 idr_remove(&battery_id, num); 382 mutex_unlock(&battery_lock); 383fail_id: 384 return ret; 385} 386 387static const struct i2c_device_id ds278x_id[] = { 388 {"ds2782", 0}, 389 {"ds2786", 1}, 390 {}, 391}; 392 393static struct i2c_driver ds278x_battery_driver = { 394 .driver = { 395 .name = "ds2782-battery", 396 }, 397 .probe = ds278x_battery_probe, 398 .remove = ds278x_battery_remove, 399 .id_table = ds278x_id, 400}; 401 402static int __init ds278x_init(void) 403{ 404 return i2c_add_driver(&ds278x_battery_driver); 405} 406module_init(ds278x_init); 407 408static void __exit ds278x_exit(void) 409{ 410 i2c_del_driver(&ds278x_battery_driver); 411} 412module_exit(ds278x_exit); 413 414MODULE_AUTHOR("Ryan Mallon <ryan@bluewatersys.com>"); 415MODULE_DESCRIPTION("Maxim/Dallas DS2782 Stand-Alone Fuel Gauage IC driver"); 416MODULE_LICENSE("GPL"); 417