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