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