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
   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
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.