Root/drivers/power/ds2782_battery.c

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, &current_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};
406module_i2c_driver(ds278x_battery_driver);
407
408MODULE_AUTHOR("Ryan Mallon");
409MODULE_DESCRIPTION("Maxim/Dallas DS2782 Stand-Alone Fuel Gauage IC driver");
410MODULE_LICENSE("GPL");
411

Archive Download this file



interactive