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