linux/drivers/power/bq20z75.c
<<
>>
Prefs
   1/*
   2 * Gas Gauge driver for TI's BQ20Z75
   3 *
   4 * Copyright (c) 2010, NVIDIA Corporation.
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License as published by
   8 * the Free Software Foundation; either version 2 of the License, or
   9 * (at your option) any later version.
  10 *
  11 * This program is distributed in the hope that it will be useful, but WITHOUT
  12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  14 * more details.
  15 *
  16 * You should have received a copy of the GNU General Public License along
  17 * with this program; if not, write to the Free Software Foundation, Inc.,
  18 * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  19 */
  20
  21#include <linux/init.h>
  22#include <linux/module.h>
  23#include <linux/kernel.h>
  24#include <linux/err.h>
  25#include <linux/power_supply.h>
  26#include <linux/i2c.h>
  27#include <linux/slab.h>
  28
  29enum {
  30        REG_MANUFACTURER_DATA,
  31        REG_TEMPERATURE,
  32        REG_VOLTAGE,
  33        REG_CURRENT,
  34        REG_CAPACITY,
  35        REG_TIME_TO_EMPTY,
  36        REG_TIME_TO_FULL,
  37        REG_STATUS,
  38        REG_CYCLE_COUNT,
  39        REG_SERIAL_NUMBER,
  40        REG_REMAINING_CAPACITY,
  41        REG_FULL_CHARGE_CAPACITY,
  42        REG_DESIGN_CAPACITY,
  43        REG_DESIGN_VOLTAGE,
  44};
  45
  46/* manufacturer access defines */
  47#define MANUFACTURER_ACCESS_STATUS      0x0006
  48#define MANUFACTURER_ACCESS_SLEEP       0x0011
  49
  50/* battery status value bits */
  51#define BATTERY_DISCHARGING             0x40
  52#define BATTERY_FULL_CHARGED            0x20
  53#define BATTERY_FULL_DISCHARGED         0x10
  54
  55#define BQ20Z75_DATA(_psp, _addr, _min_value, _max_value) { \
  56        .psp = _psp, \
  57        .addr = _addr, \
  58        .min_value = _min_value, \
  59        .max_value = _max_value, \
  60}
  61
  62static const struct bq20z75_device_data {
  63        enum power_supply_property psp;
  64        u8 addr;
  65        int min_value;
  66        int max_value;
  67} bq20z75_data[] = {
  68        [REG_MANUFACTURER_DATA] =
  69                BQ20Z75_DATA(POWER_SUPPLY_PROP_PRESENT, 0x00, 0, 65535),
  70        [REG_TEMPERATURE] =
  71                BQ20Z75_DATA(POWER_SUPPLY_PROP_TEMP, 0x08, 0, 65535),
  72        [REG_VOLTAGE] =
  73                BQ20Z75_DATA(POWER_SUPPLY_PROP_VOLTAGE_NOW, 0x09, 0, 20000),
  74        [REG_CURRENT] =
  75                BQ20Z75_DATA(POWER_SUPPLY_PROP_CURRENT_NOW, 0x0A, -32768,
  76                        32767),
  77        [REG_CAPACITY] =
  78                BQ20Z75_DATA(POWER_SUPPLY_PROP_CAPACITY, 0x0E, 0, 100),
  79        [REG_REMAINING_CAPACITY] =
  80                BQ20Z75_DATA(POWER_SUPPLY_PROP_ENERGY_NOW, 0x0F, 0, 65535),
  81        [REG_FULL_CHARGE_CAPACITY] =
  82                BQ20Z75_DATA(POWER_SUPPLY_PROP_ENERGY_FULL, 0x10, 0, 65535),
  83        [REG_TIME_TO_EMPTY] =
  84                BQ20Z75_DATA(POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, 0x12, 0,
  85                        65535),
  86        [REG_TIME_TO_FULL] =
  87                BQ20Z75_DATA(POWER_SUPPLY_PROP_TIME_TO_FULL_AVG, 0x13, 0,
  88                        65535),
  89        [REG_STATUS] =
  90                BQ20Z75_DATA(POWER_SUPPLY_PROP_STATUS, 0x16, 0, 65535),
  91        [REG_CYCLE_COUNT] =
  92                BQ20Z75_DATA(POWER_SUPPLY_PROP_CYCLE_COUNT, 0x17, 0, 65535),
  93        [REG_DESIGN_CAPACITY] =
  94                BQ20Z75_DATA(POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, 0x18, 0,
  95                        65535),
  96        [REG_DESIGN_VOLTAGE] =
  97                BQ20Z75_DATA(POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, 0x19, 0,
  98                        65535),
  99        [REG_SERIAL_NUMBER] =
 100                BQ20Z75_DATA(POWER_SUPPLY_PROP_SERIAL_NUMBER, 0x1C, 0, 65535),
 101};
 102
 103static enum power_supply_property bq20z75_properties[] = {
 104        POWER_SUPPLY_PROP_STATUS,
 105        POWER_SUPPLY_PROP_HEALTH,
 106        POWER_SUPPLY_PROP_PRESENT,
 107        POWER_SUPPLY_PROP_TECHNOLOGY,
 108        POWER_SUPPLY_PROP_CYCLE_COUNT,
 109        POWER_SUPPLY_PROP_VOLTAGE_NOW,
 110        POWER_SUPPLY_PROP_CURRENT_NOW,
 111        POWER_SUPPLY_PROP_CAPACITY,
 112        POWER_SUPPLY_PROP_TEMP,
 113        POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
 114        POWER_SUPPLY_PROP_TIME_TO_FULL_AVG,
 115        POWER_SUPPLY_PROP_SERIAL_NUMBER,
 116        POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
 117        POWER_SUPPLY_PROP_ENERGY_NOW,
 118        POWER_SUPPLY_PROP_ENERGY_FULL,
 119        POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN,
 120};
 121
 122struct bq20z75_info {
 123        struct i2c_client       *client;
 124        struct power_supply     power_supply;
 125};
 126
 127static int bq20z75_read_word_data(struct i2c_client *client, u8 address)
 128{
 129        s32 ret;
 130
 131        ret = i2c_smbus_read_word_data(client, address);
 132        if (ret < 0) {
 133                dev_err(&client->dev,
 134                        "%s: i2c read at address 0x%x failed\n",
 135                        __func__, address);
 136                return ret;
 137        }
 138        return le16_to_cpu(ret);
 139}
 140
 141static int bq20z75_write_word_data(struct i2c_client *client, u8 address,
 142        u16 value)
 143{
 144        s32 ret;
 145
 146        ret = i2c_smbus_write_word_data(client, address, le16_to_cpu(value));
 147        if (ret < 0) {
 148                dev_err(&client->dev,
 149                        "%s: i2c write to address 0x%x failed\n",
 150                        __func__, address);
 151                return ret;
 152        }
 153        return 0;
 154}
 155
 156static int bq20z75_get_battery_presence_and_health(
 157        struct i2c_client *client, enum power_supply_property psp,
 158        union power_supply_propval *val)
 159{
 160        s32 ret;
 161
 162        /* Write to ManufacturerAccess with
 163         * ManufacturerAccess command and then
 164         * read the status */
 165        ret = bq20z75_write_word_data(client,
 166                bq20z75_data[REG_MANUFACTURER_DATA].addr,
 167                MANUFACTURER_ACCESS_STATUS);
 168        if (ret < 0)
 169                return ret;
 170
 171
 172        ret = bq20z75_read_word_data(client,
 173                bq20z75_data[REG_MANUFACTURER_DATA].addr);
 174        if (ret < 0)
 175                return ret;
 176
 177        if (ret < bq20z75_data[REG_MANUFACTURER_DATA].min_value ||
 178            ret > bq20z75_data[REG_MANUFACTURER_DATA].max_value) {
 179                val->intval = 0;
 180                return 0;
 181        }
 182
 183        /* Mask the upper nibble of 2nd byte and
 184         * lower byte of response then
 185         * shift the result by 8 to get status*/
 186        ret &= 0x0F00;
 187        ret >>= 8;
 188        if (psp == POWER_SUPPLY_PROP_PRESENT) {
 189                if (ret == 0x0F)
 190                        /* battery removed */
 191                        val->intval = 0;
 192                else
 193                        val->intval = 1;
 194        } else if (psp == POWER_SUPPLY_PROP_HEALTH) {
 195                if (ret == 0x09)
 196                        val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
 197                else if (ret == 0x0B)
 198                        val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
 199                else if (ret == 0x0C)
 200                        val->intval = POWER_SUPPLY_HEALTH_DEAD;
 201                else
 202                        val->intval = POWER_SUPPLY_HEALTH_GOOD;
 203        }
 204
 205        return 0;
 206}
 207
 208static int bq20z75_get_battery_property(struct i2c_client *client,
 209        int reg_offset, enum power_supply_property psp,
 210        union power_supply_propval *val)
 211{
 212        s32 ret;
 213
 214        ret = bq20z75_read_word_data(client,
 215                bq20z75_data[reg_offset].addr);
 216        if (ret < 0)
 217                return ret;
 218
 219        /* returned values are 16 bit */
 220        if (bq20z75_data[reg_offset].min_value < 0)
 221                ret = (s16)ret;
 222
 223        if (ret >= bq20z75_data[reg_offset].min_value &&
 224            ret <= bq20z75_data[reg_offset].max_value) {
 225                val->intval = ret;
 226                if (psp == POWER_SUPPLY_PROP_STATUS) {
 227                        if (ret & BATTERY_FULL_CHARGED)
 228                                val->intval = POWER_SUPPLY_STATUS_FULL;
 229                        else if (ret & BATTERY_FULL_DISCHARGED)
 230                                val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
 231                        else if (ret & BATTERY_DISCHARGING)
 232                                val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
 233                        else
 234                                val->intval = POWER_SUPPLY_STATUS_CHARGING;
 235                }
 236        } else {
 237                if (psp == POWER_SUPPLY_PROP_STATUS)
 238                        val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
 239                else
 240                        val->intval = 0;
 241        }
 242
 243        return 0;
 244}
 245
 246static void  bq20z75_unit_adjustment(struct i2c_client *client,
 247        enum power_supply_property psp, union power_supply_propval *val)
 248{
 249#define BASE_UNIT_CONVERSION            1000
 250#define BATTERY_MODE_CAP_MULT_WATT      (10 * BASE_UNIT_CONVERSION)
 251#define TIME_UNIT_CONVERSION            600
 252#define TEMP_KELVIN_TO_CELCIUS          2731
 253        switch (psp) {
 254        case POWER_SUPPLY_PROP_ENERGY_NOW:
 255        case POWER_SUPPLY_PROP_ENERGY_FULL:
 256        case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN:
 257                val->intval *= BATTERY_MODE_CAP_MULT_WATT;
 258                break;
 259
 260        case POWER_SUPPLY_PROP_VOLTAGE_NOW:
 261        case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
 262        case POWER_SUPPLY_PROP_CURRENT_NOW:
 263                val->intval *= BASE_UNIT_CONVERSION;
 264                break;
 265
 266        case POWER_SUPPLY_PROP_TEMP:
 267                /* bq20z75 provides battery tempreture in 0.1°K
 268                 * so convert it to 0.1°C */
 269                val->intval -= TEMP_KELVIN_TO_CELCIUS;
 270                val->intval *= 10;
 271                break;
 272
 273        case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG:
 274        case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG:
 275                val->intval *= TIME_UNIT_CONVERSION;
 276                break;
 277
 278        default:
 279                dev_dbg(&client->dev,
 280                        "%s: no need for unit conversion %d\n", __func__, psp);
 281        }
 282}
 283
 284static int bq20z75_get_battery_capacity(struct i2c_client *client,
 285        int reg_offset, enum power_supply_property psp,
 286        union power_supply_propval *val)
 287{
 288        s32 ret;
 289
 290        ret = bq20z75_read_word_data(client, bq20z75_data[reg_offset].addr);
 291        if (ret < 0)
 292                return ret;
 293
 294        if (psp == POWER_SUPPLY_PROP_CAPACITY) {
 295                /* bq20z75 spec says that this can be >100 %
 296                * even if max value is 100 % */
 297                val->intval = min(ret, 100);
 298        } else
 299                val->intval = ret;
 300
 301        return 0;
 302}
 303
 304static char bq20z75_serial[5];
 305static int bq20z75_get_battery_serial_number(struct i2c_client *client,
 306        union power_supply_propval *val)
 307{
 308        int ret;
 309
 310        ret = bq20z75_read_word_data(client,
 311                bq20z75_data[REG_SERIAL_NUMBER].addr);
 312        if (ret < 0)
 313                return ret;
 314
 315        ret = sprintf(bq20z75_serial, "%04x", ret);
 316        val->strval = bq20z75_serial;
 317
 318        return 0;
 319}
 320
 321static int bq20z75_get_property(struct power_supply *psy,
 322        enum power_supply_property psp,
 323        union power_supply_propval *val)
 324{
 325        int count;
 326        int ret;
 327        struct bq20z75_info *bq20z75_device = container_of(psy,
 328                                struct bq20z75_info, power_supply);
 329        struct i2c_client *client = bq20z75_device->client;
 330
 331        switch (psp) {
 332        case POWER_SUPPLY_PROP_PRESENT:
 333        case POWER_SUPPLY_PROP_HEALTH:
 334                ret = bq20z75_get_battery_presence_and_health(client, psp, val);
 335                if (ret)
 336                        return ret;
 337                break;
 338
 339        case POWER_SUPPLY_PROP_TECHNOLOGY:
 340                val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
 341                break;
 342
 343        case POWER_SUPPLY_PROP_ENERGY_NOW:
 344        case POWER_SUPPLY_PROP_ENERGY_FULL:
 345        case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN:
 346        case POWER_SUPPLY_PROP_CAPACITY:
 347                for (count = 0; count < ARRAY_SIZE(bq20z75_data); count++) {
 348                        if (psp == bq20z75_data[count].psp)
 349                                break;
 350                }
 351
 352                ret = bq20z75_get_battery_capacity(client, count, psp, val);
 353                if (ret)
 354                        return ret;
 355
 356                break;
 357
 358        case POWER_SUPPLY_PROP_SERIAL_NUMBER:
 359                ret = bq20z75_get_battery_serial_number(client, val);
 360                if (ret)
 361                        return ret;
 362                break;
 363
 364        case POWER_SUPPLY_PROP_STATUS:
 365        case POWER_SUPPLY_PROP_CYCLE_COUNT:
 366        case POWER_SUPPLY_PROP_VOLTAGE_NOW:
 367        case POWER_SUPPLY_PROP_CURRENT_NOW:
 368        case POWER_SUPPLY_PROP_TEMP:
 369        case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG:
 370        case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG:
 371        case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
 372                for (count = 0; count < ARRAY_SIZE(bq20z75_data); count++) {
 373                        if (psp == bq20z75_data[count].psp)
 374                                break;
 375                }
 376
 377                ret = bq20z75_get_battery_property(client, count, psp, val);
 378                if (ret)
 379                        return ret;
 380
 381                break;
 382
 383        default:
 384                dev_err(&client->dev,
 385                        "%s: INVALID property\n", __func__);
 386                return -EINVAL;
 387        }
 388
 389        /* Convert units to match requirements for power supply class */
 390        bq20z75_unit_adjustment(client, psp, val);
 391
 392        dev_dbg(&client->dev,
 393                "%s: property = %d, value = %d\n", __func__, psp, val->intval);
 394
 395        return 0;
 396}
 397
 398static int bq20z75_probe(struct i2c_client *client,
 399        const struct i2c_device_id *id)
 400{
 401        struct bq20z75_info *bq20z75_device;
 402        int rc;
 403
 404        bq20z75_device = kzalloc(sizeof(struct bq20z75_info), GFP_KERNEL);
 405        if (!bq20z75_device)
 406                return -ENOMEM;
 407
 408        bq20z75_device->client = client;
 409        bq20z75_device->power_supply.name = "battery";
 410        bq20z75_device->power_supply.type = POWER_SUPPLY_TYPE_BATTERY;
 411        bq20z75_device->power_supply.properties = bq20z75_properties;
 412        bq20z75_device->power_supply.num_properties =
 413                ARRAY_SIZE(bq20z75_properties);
 414        bq20z75_device->power_supply.get_property = bq20z75_get_property;
 415
 416        i2c_set_clientdata(client, bq20z75_device);
 417
 418        rc = power_supply_register(&client->dev, &bq20z75_device->power_supply);
 419        if (rc) {
 420                dev_err(&client->dev,
 421                        "%s: Failed to register power supply\n", __func__);
 422                kfree(bq20z75_device);
 423                return rc;
 424        }
 425
 426        dev_info(&client->dev,
 427                "%s: battery gas gauge device registered\n", client->name);
 428
 429        return 0;
 430}
 431
 432static int bq20z75_remove(struct i2c_client *client)
 433{
 434        struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client);
 435
 436        power_supply_unregister(&bq20z75_device->power_supply);
 437        kfree(bq20z75_device);
 438        bq20z75_device = NULL;
 439
 440        return 0;
 441}
 442
 443#if defined CONFIG_PM
 444static int bq20z75_suspend(struct i2c_client *client,
 445        pm_message_t state)
 446{
 447        s32 ret;
 448
 449        /* write to manufacturer access with sleep command */
 450        ret = bq20z75_write_word_data(client,
 451                bq20z75_data[REG_MANUFACTURER_DATA].addr,
 452                MANUFACTURER_ACCESS_SLEEP);
 453        if (ret < 0)
 454                return ret;
 455
 456        return 0;
 457}
 458#else
 459#define bq20z75_suspend         NULL
 460#endif
 461/* any smbus transaction will wake up bq20z75 */
 462#define bq20z75_resume          NULL
 463
 464static const struct i2c_device_id bq20z75_id[] = {
 465        { "bq20z75", 0 },
 466        {}
 467};
 468
 469static struct i2c_driver bq20z75_battery_driver = {
 470        .probe          = bq20z75_probe,
 471        .remove         = bq20z75_remove,
 472        .suspend        = bq20z75_suspend,
 473        .resume         = bq20z75_resume,
 474        .id_table       = bq20z75_id,
 475        .driver = {
 476                .name   = "bq20z75-battery",
 477        },
 478};
 479
 480static int __init bq20z75_battery_init(void)
 481{
 482        return i2c_add_driver(&bq20z75_battery_driver);
 483}
 484module_init(bq20z75_battery_init);
 485
 486static void __exit bq20z75_battery_exit(void)
 487{
 488        i2c_del_driver(&bq20z75_battery_driver);
 489}
 490module_exit(bq20z75_battery_exit);
 491
 492MODULE_DESCRIPTION("BQ20z75 battery monitor driver");
 493MODULE_LICENSE("GPL");
 494