linux/drivers/power/supply/axp288_fuel_gauge.c
<<
>>
Prefs
   1/*
   2 * axp288_fuel_gauge.c - Xpower AXP288 PMIC Fuel Gauge Driver
   3 *
   4 * Copyright (C) 2014 Intel Corporation
   5 *
   6 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   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 as published by
  10 * the Free Software Foundation; version 2 of the License.
  11 *
  12 * This program is distributed in the hope that it will be useful, but
  13 * WITHOUT ANY WARRANTY; without even the implied warranty of
  14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15 * General Public License for more details.
  16 *
  17 */
  18
  19#include <linux/module.h>
  20#include <linux/kernel.h>
  21#include <linux/device.h>
  22#include <linux/regmap.h>
  23#include <linux/jiffies.h>
  24#include <linux/interrupt.h>
  25#include <linux/workqueue.h>
  26#include <linux/mfd/axp20x.h>
  27#include <linux/platform_device.h>
  28#include <linux/power_supply.h>
  29#include <linux/iio/consumer.h>
  30#include <linux/debugfs.h>
  31#include <linux/seq_file.h>
  32
  33#define CHRG_STAT_BAT_SAFE_MODE         (1 << 3)
  34#define CHRG_STAT_BAT_VALID                     (1 << 4)
  35#define CHRG_STAT_BAT_PRESENT           (1 << 5)
  36#define CHRG_STAT_CHARGING                      (1 << 6)
  37#define CHRG_STAT_PMIC_OTP                      (1 << 7)
  38
  39#define CHRG_CCCV_CC_MASK                       0xf     /* 4 bits */
  40#define CHRG_CCCV_CC_BIT_POS            0
  41#define CHRG_CCCV_CC_OFFSET                     200     /* 200mA */
  42#define CHRG_CCCV_CC_LSB_RES            200     /* 200mA */
  43#define CHRG_CCCV_ITERM_20P                     (1 << 4)    /* 20% of CC */
  44#define CHRG_CCCV_CV_MASK                       0x60        /* 2 bits */
  45#define CHRG_CCCV_CV_BIT_POS            5
  46#define CHRG_CCCV_CV_4100MV                     0x0     /* 4.10V */
  47#define CHRG_CCCV_CV_4150MV                     0x1     /* 4.15V */
  48#define CHRG_CCCV_CV_4200MV                     0x2     /* 4.20V */
  49#define CHRG_CCCV_CV_4350MV                     0x3     /* 4.35V */
  50#define CHRG_CCCV_CHG_EN                        (1 << 7)
  51
  52#define CV_4100                                         4100    /* 4100mV */
  53#define CV_4150                                         4150    /* 4150mV */
  54#define CV_4200                                         4200    /* 4200mV */
  55#define CV_4350                                         4350    /* 4350mV */
  56
  57#define TEMP_IRQ_CFG_QWBTU                      (1 << 0)
  58#define TEMP_IRQ_CFG_WBTU                       (1 << 1)
  59#define TEMP_IRQ_CFG_QWBTO                      (1 << 2)
  60#define TEMP_IRQ_CFG_WBTO                       (1 << 3)
  61#define TEMP_IRQ_CFG_MASK                       0xf
  62
  63#define FG_IRQ_CFG_LOWBATT_WL2          (1 << 0)
  64#define FG_IRQ_CFG_LOWBATT_WL1          (1 << 1)
  65#define FG_IRQ_CFG_LOWBATT_MASK         0x3
  66#define LOWBAT_IRQ_STAT_LOWBATT_WL2     (1 << 0)
  67#define LOWBAT_IRQ_STAT_LOWBATT_WL1     (1 << 1)
  68
  69#define FG_CNTL_OCV_ADJ_STAT            (1 << 2)
  70#define FG_CNTL_OCV_ADJ_EN                      (1 << 3)
  71#define FG_CNTL_CAP_ADJ_STAT            (1 << 4)
  72#define FG_CNTL_CAP_ADJ_EN                      (1 << 5)
  73#define FG_CNTL_CC_EN                           (1 << 6)
  74#define FG_CNTL_GAUGE_EN                        (1 << 7)
  75
  76#define FG_REP_CAP_VALID                        (1 << 7)
  77#define FG_REP_CAP_VAL_MASK                     0x7F
  78
  79#define FG_DES_CAP1_VALID                       (1 << 7)
  80#define FG_DES_CAP1_VAL_MASK            0x7F
  81#define FG_DES_CAP0_VAL_MASK            0xFF
  82#define FG_DES_CAP_RES_LSB                      1456    /* 1.456mAhr */
  83
  84#define FG_CC_MTR1_VALID                        (1 << 7)
  85#define FG_CC_MTR1_VAL_MASK                     0x7F
  86#define FG_CC_MTR0_VAL_MASK                     0xFF
  87#define FG_DES_CC_RES_LSB                       1456    /* 1.456mAhr */
  88
  89#define FG_OCV_CAP_VALID                        (1 << 7)
  90#define FG_OCV_CAP_VAL_MASK                     0x7F
  91#define FG_CC_CAP_VALID                         (1 << 7)
  92#define FG_CC_CAP_VAL_MASK                      0x7F
  93
  94#define FG_LOW_CAP_THR1_MASK            0xf0    /* 5% tp 20% */
  95#define FG_LOW_CAP_THR1_VAL                     0xa0    /* 15 perc */
  96#define FG_LOW_CAP_THR2_MASK            0x0f    /* 0% to 15% */
  97#define FG_LOW_CAP_WARN_THR                     14  /* 14 perc */
  98#define FG_LOW_CAP_CRIT_THR                     4   /* 4 perc */
  99#define FG_LOW_CAP_SHDN_THR                     0   /* 0 perc */
 100
 101#define STATUS_MON_DELAY_JIFFIES    (HZ * 60)   /*60 sec */
 102#define NR_RETRY_CNT    3
 103#define DEV_NAME        "axp288_fuel_gauge"
 104
 105/* 1.1mV per LSB expressed in uV */
 106#define VOLTAGE_FROM_ADC(a)                     ((a * 11) / 10)
 107/* properties converted to tenths of degrees, uV, uA, uW */
 108#define PROP_TEMP(a)            ((a) * 10)
 109#define UNPROP_TEMP(a)          ((a) / 10)
 110#define PROP_VOLT(a)            ((a) * 1000)
 111#define PROP_CURR(a)            ((a) * 1000)
 112
 113#define AXP288_FG_INTR_NUM      6
 114enum {
 115        QWBTU_IRQ = 0,
 116        WBTU_IRQ,
 117        QWBTO_IRQ,
 118        WBTO_IRQ,
 119        WL2_IRQ,
 120        WL1_IRQ,
 121};
 122
 123struct axp288_fg_info {
 124        struct platform_device *pdev;
 125        struct axp20x_fg_pdata *pdata;
 126        struct regmap *regmap;
 127        struct regmap_irq_chip_data *regmap_irqc;
 128        int irq[AXP288_FG_INTR_NUM];
 129        struct power_supply *bat;
 130        struct mutex lock;
 131        int status;
 132        struct delayed_work status_monitor;
 133        struct dentry *debug_file;
 134};
 135
 136static enum power_supply_property fuel_gauge_props[] = {
 137        POWER_SUPPLY_PROP_STATUS,
 138        POWER_SUPPLY_PROP_PRESENT,
 139        POWER_SUPPLY_PROP_HEALTH,
 140        POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
 141        POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
 142        POWER_SUPPLY_PROP_VOLTAGE_NOW,
 143        POWER_SUPPLY_PROP_VOLTAGE_OCV,
 144        POWER_SUPPLY_PROP_CURRENT_NOW,
 145        POWER_SUPPLY_PROP_CAPACITY,
 146        POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN,
 147        POWER_SUPPLY_PROP_TEMP,
 148        POWER_SUPPLY_PROP_TEMP_MAX,
 149        POWER_SUPPLY_PROP_TEMP_MIN,
 150        POWER_SUPPLY_PROP_TEMP_ALERT_MIN,
 151        POWER_SUPPLY_PROP_TEMP_ALERT_MAX,
 152        POWER_SUPPLY_PROP_TECHNOLOGY,
 153        POWER_SUPPLY_PROP_CHARGE_FULL,
 154        POWER_SUPPLY_PROP_CHARGE_NOW,
 155        POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
 156        POWER_SUPPLY_PROP_MODEL_NAME,
 157};
 158
 159static int fuel_gauge_reg_readb(struct axp288_fg_info *info, int reg)
 160{
 161        int ret, i;
 162        unsigned int val;
 163
 164        for (i = 0; i < NR_RETRY_CNT; i++) {
 165                ret = regmap_read(info->regmap, reg, &val);
 166                if (ret == -EBUSY)
 167                        continue;
 168                else
 169                        break;
 170        }
 171
 172        if (ret < 0)
 173                dev_err(&info->pdev->dev, "axp288 reg read err:%d\n", ret);
 174
 175        return val;
 176}
 177
 178static int fuel_gauge_reg_writeb(struct axp288_fg_info *info, int reg, u8 val)
 179{
 180        int ret;
 181
 182        ret = regmap_write(info->regmap, reg, (unsigned int)val);
 183
 184        if (ret < 0)
 185                dev_err(&info->pdev->dev, "axp288 reg write err:%d\n", ret);
 186
 187        return ret;
 188}
 189
 190static int pmic_read_adc_val(const char *name, int *raw_val,
 191                struct axp288_fg_info *info)
 192{
 193        int ret, val = 0;
 194        struct iio_channel *indio_chan;
 195
 196        indio_chan = iio_channel_get(NULL, name);
 197        if (IS_ERR_OR_NULL(indio_chan)) {
 198                ret = PTR_ERR(indio_chan);
 199                goto exit;
 200        }
 201        ret = iio_read_channel_raw(indio_chan, &val);
 202        if (ret < 0) {
 203                dev_err(&info->pdev->dev,
 204                        "IIO channel read error: %x, %x\n", ret, val);
 205                goto err_exit;
 206        }
 207
 208        dev_dbg(&info->pdev->dev, "adc raw val=%x\n", val);
 209        *raw_val = val;
 210
 211err_exit:
 212        iio_channel_release(indio_chan);
 213exit:
 214        return ret;
 215}
 216
 217#ifdef CONFIG_DEBUG_FS
 218static int fuel_gauge_debug_show(struct seq_file *s, void *data)
 219{
 220        struct axp288_fg_info *info = s->private;
 221        int raw_val, ret;
 222
 223        seq_printf(s, " PWR_STATUS[%02x] : %02x\n",
 224                AXP20X_PWR_INPUT_STATUS,
 225                fuel_gauge_reg_readb(info, AXP20X_PWR_INPUT_STATUS));
 226        seq_printf(s, "PWR_OP_MODE[%02x] : %02x\n",
 227                AXP20X_PWR_OP_MODE,
 228                fuel_gauge_reg_readb(info, AXP20X_PWR_OP_MODE));
 229        seq_printf(s, " CHRG_CTRL1[%02x] : %02x\n",
 230                AXP20X_CHRG_CTRL1,
 231                fuel_gauge_reg_readb(info, AXP20X_CHRG_CTRL1));
 232        seq_printf(s, "       VLTF[%02x] : %02x\n",
 233                AXP20X_V_LTF_DISCHRG,
 234                fuel_gauge_reg_readb(info, AXP20X_V_LTF_DISCHRG));
 235        seq_printf(s, "       VHTF[%02x] : %02x\n",
 236                AXP20X_V_HTF_DISCHRG,
 237                fuel_gauge_reg_readb(info, AXP20X_V_HTF_DISCHRG));
 238        seq_printf(s, "    CC_CTRL[%02x] : %02x\n",
 239                AXP20X_CC_CTRL,
 240                fuel_gauge_reg_readb(info, AXP20X_CC_CTRL));
 241        seq_printf(s, "BATTERY CAP[%02x] : %02x\n",
 242                AXP20X_FG_RES,
 243                fuel_gauge_reg_readb(info, AXP20X_FG_RES));
 244        seq_printf(s, "    FG_RDC1[%02x] : %02x\n",
 245                AXP288_FG_RDC1_REG,
 246                fuel_gauge_reg_readb(info, AXP288_FG_RDC1_REG));
 247        seq_printf(s, "    FG_RDC0[%02x] : %02x\n",
 248                AXP288_FG_RDC0_REG,
 249                fuel_gauge_reg_readb(info, AXP288_FG_RDC0_REG));
 250        seq_printf(s, "    FG_OCVH[%02x] : %02x\n",
 251                AXP288_FG_OCVH_REG,
 252                fuel_gauge_reg_readb(info, AXP288_FG_OCVH_REG));
 253        seq_printf(s, "    FG_OCVL[%02x] : %02x\n",
 254                AXP288_FG_OCVL_REG,
 255                fuel_gauge_reg_readb(info, AXP288_FG_OCVL_REG));
 256        seq_printf(s, "FG_DES_CAP1[%02x] : %02x\n",
 257                AXP288_FG_DES_CAP1_REG,
 258                fuel_gauge_reg_readb(info, AXP288_FG_DES_CAP1_REG));
 259        seq_printf(s, "FG_DES_CAP0[%02x] : %02x\n",
 260                AXP288_FG_DES_CAP0_REG,
 261                fuel_gauge_reg_readb(info, AXP288_FG_DES_CAP0_REG));
 262        seq_printf(s, " FG_CC_MTR1[%02x] : %02x\n",
 263                AXP288_FG_CC_MTR1_REG,
 264                fuel_gauge_reg_readb(info, AXP288_FG_CC_MTR1_REG));
 265        seq_printf(s, " FG_CC_MTR0[%02x] : %02x\n",
 266                AXP288_FG_CC_MTR0_REG,
 267                fuel_gauge_reg_readb(info, AXP288_FG_CC_MTR0_REG));
 268        seq_printf(s, " FG_OCV_CAP[%02x] : %02x\n",
 269                AXP288_FG_OCV_CAP_REG,
 270                fuel_gauge_reg_readb(info, AXP288_FG_OCV_CAP_REG));
 271        seq_printf(s, "  FG_CC_CAP[%02x] : %02x\n",
 272                AXP288_FG_CC_CAP_REG,
 273                fuel_gauge_reg_readb(info, AXP288_FG_CC_CAP_REG));
 274        seq_printf(s, " FG_LOW_CAP[%02x] : %02x\n",
 275                AXP288_FG_LOW_CAP_REG,
 276                fuel_gauge_reg_readb(info, AXP288_FG_LOW_CAP_REG));
 277        seq_printf(s, "TUNING_CTL0[%02x] : %02x\n",
 278                AXP288_FG_TUNE0,
 279                fuel_gauge_reg_readb(info, AXP288_FG_TUNE0));
 280        seq_printf(s, "TUNING_CTL1[%02x] : %02x\n",
 281                AXP288_FG_TUNE1,
 282                fuel_gauge_reg_readb(info, AXP288_FG_TUNE1));
 283        seq_printf(s, "TUNING_CTL2[%02x] : %02x\n",
 284                AXP288_FG_TUNE2,
 285                fuel_gauge_reg_readb(info, AXP288_FG_TUNE2));
 286        seq_printf(s, "TUNING_CTL3[%02x] : %02x\n",
 287                AXP288_FG_TUNE3,
 288                fuel_gauge_reg_readb(info, AXP288_FG_TUNE3));
 289        seq_printf(s, "TUNING_CTL4[%02x] : %02x\n",
 290                AXP288_FG_TUNE4,
 291                fuel_gauge_reg_readb(info, AXP288_FG_TUNE4));
 292        seq_printf(s, "TUNING_CTL5[%02x] : %02x\n",
 293                AXP288_FG_TUNE5,
 294                fuel_gauge_reg_readb(info, AXP288_FG_TUNE5));
 295
 296        ret = pmic_read_adc_val("axp288-batt-temp", &raw_val, info);
 297        if (ret >= 0)
 298                seq_printf(s, "axp288-batttemp : %d\n", raw_val);
 299        ret = pmic_read_adc_val("axp288-pmic-temp", &raw_val, info);
 300        if (ret >= 0)
 301                seq_printf(s, "axp288-pmictemp : %d\n", raw_val);
 302        ret = pmic_read_adc_val("axp288-system-temp", &raw_val, info);
 303        if (ret >= 0)
 304                seq_printf(s, "axp288-systtemp : %d\n", raw_val);
 305        ret = pmic_read_adc_val("axp288-chrg-curr", &raw_val, info);
 306        if (ret >= 0)
 307                seq_printf(s, "axp288-chrgcurr : %d\n", raw_val);
 308        ret = pmic_read_adc_val("axp288-chrg-d-curr", &raw_val, info);
 309        if (ret >= 0)
 310                seq_printf(s, "axp288-dchrgcur : %d\n", raw_val);
 311        ret = pmic_read_adc_val("axp288-batt-volt", &raw_val, info);
 312        if (ret >= 0)
 313                seq_printf(s, "axp288-battvolt : %d\n", raw_val);
 314
 315        return 0;
 316}
 317
 318static int debug_open(struct inode *inode, struct file *file)
 319{
 320        return single_open(file, fuel_gauge_debug_show, inode->i_private);
 321}
 322
 323static const struct file_operations fg_debug_fops = {
 324        .open       = debug_open,
 325        .read       = seq_read,
 326        .llseek     = seq_lseek,
 327        .release    = single_release,
 328};
 329
 330static void fuel_gauge_create_debugfs(struct axp288_fg_info *info)
 331{
 332        info->debug_file = debugfs_create_file("fuelgauge", 0666, NULL,
 333                info, &fg_debug_fops);
 334}
 335
 336static void fuel_gauge_remove_debugfs(struct axp288_fg_info *info)
 337{
 338        debugfs_remove(info->debug_file);
 339}
 340#else
 341static inline void fuel_gauge_create_debugfs(struct axp288_fg_info *info)
 342{
 343}
 344static inline void fuel_gauge_remove_debugfs(struct axp288_fg_info *info)
 345{
 346}
 347#endif
 348
 349static void fuel_gauge_get_status(struct axp288_fg_info *info)
 350{
 351        int pwr_stat, ret;
 352        int charge, discharge;
 353
 354        pwr_stat = fuel_gauge_reg_readb(info, AXP20X_PWR_INPUT_STATUS);
 355        if (pwr_stat < 0) {
 356                dev_err(&info->pdev->dev,
 357                        "PWR STAT read failed:%d\n", pwr_stat);
 358                return;
 359        }
 360        ret = pmic_read_adc_val("axp288-chrg-curr", &charge, info);
 361        if (ret < 0) {
 362                dev_err(&info->pdev->dev,
 363                        "ADC charge current read failed:%d\n", ret);
 364                return;
 365        }
 366        ret = pmic_read_adc_val("axp288-chrg-d-curr", &discharge, info);
 367        if (ret < 0) {
 368                dev_err(&info->pdev->dev,
 369                        "ADC discharge current read failed:%d\n", ret);
 370                return;
 371        }
 372
 373        if (charge > 0)
 374                info->status = POWER_SUPPLY_STATUS_CHARGING;
 375        else if (discharge > 0)
 376                info->status = POWER_SUPPLY_STATUS_DISCHARGING;
 377        else {
 378                if (pwr_stat & CHRG_STAT_BAT_PRESENT)
 379                        info->status = POWER_SUPPLY_STATUS_FULL;
 380                else
 381                        info->status = POWER_SUPPLY_STATUS_NOT_CHARGING;
 382        }
 383}
 384
 385static int fuel_gauge_get_vbatt(struct axp288_fg_info *info, int *vbatt)
 386{
 387        int ret = 0, raw_val;
 388
 389        ret = pmic_read_adc_val("axp288-batt-volt", &raw_val, info);
 390        if (ret < 0)
 391                goto vbatt_read_fail;
 392
 393        *vbatt = VOLTAGE_FROM_ADC(raw_val);
 394vbatt_read_fail:
 395        return ret;
 396}
 397
 398static int fuel_gauge_get_current(struct axp288_fg_info *info, int *cur)
 399{
 400        int ret, value = 0;
 401        int charge, discharge;
 402
 403        ret = pmic_read_adc_val("axp288-chrg-curr", &charge, info);
 404        if (ret < 0)
 405                goto current_read_fail;
 406        ret = pmic_read_adc_val("axp288-chrg-d-curr", &discharge, info);
 407        if (ret < 0)
 408                goto current_read_fail;
 409
 410        if (charge > 0)
 411                value = charge;
 412        else if (discharge > 0)
 413                value = -1 * discharge;
 414
 415        *cur = value;
 416current_read_fail:
 417        return ret;
 418}
 419
 420static int temp_to_adc(struct axp288_fg_info *info, int tval)
 421{
 422        int rntc = 0, i, ret, adc_val;
 423        int rmin, rmax, tmin, tmax;
 424        int tcsz = info->pdata->tcsz;
 425
 426        /* get the Rntc resitance value for this temp */
 427        if (tval > info->pdata->thermistor_curve[0][1]) {
 428                rntc = info->pdata->thermistor_curve[0][0];
 429        } else if (tval <= info->pdata->thermistor_curve[tcsz-1][1]) {
 430                rntc = info->pdata->thermistor_curve[tcsz-1][0];
 431        } else {
 432                for (i = 1; i < tcsz; i++) {
 433                        if (tval > info->pdata->thermistor_curve[i][1]) {
 434                                rmin = info->pdata->thermistor_curve[i-1][0];
 435                                rmax = info->pdata->thermistor_curve[i][0];
 436                                tmin = info->pdata->thermistor_curve[i-1][1];
 437                                tmax = info->pdata->thermistor_curve[i][1];
 438                                rntc = rmin + ((rmax - rmin) *
 439                                        (tval - tmin) / (tmax - tmin));
 440                                break;
 441                        }
 442                }
 443        }
 444
 445        /* we need the current to calculate the proper adc voltage */
 446        ret = fuel_gauge_reg_readb(info, AXP20X_ADC_RATE);
 447        if (ret < 0) {
 448                dev_err(&info->pdev->dev, "%s:read err:%d\n", __func__, ret);
 449                ret = 0x30;
 450        }
 451
 452        /*
 453         * temperature is proportional to NTS thermistor resistance
 454         * ADC_RATE[5-4] determines current, 00=20uA,01=40uA,10=60uA,11=80uA
 455         * [12-bit ADC VAL] = R_NTC(Ω) * current / 800
 456         */
 457        adc_val = rntc * (20 + (20 * ((ret >> 4) & 0x3))) / 800;
 458
 459        return adc_val;
 460}
 461
 462static int adc_to_temp(struct axp288_fg_info *info, int adc_val)
 463{
 464        int ret, r, i, tval = 0;
 465        int rmin, rmax, tmin, tmax;
 466        int tcsz = info->pdata->tcsz;
 467
 468        ret = fuel_gauge_reg_readb(info, AXP20X_ADC_RATE);
 469        if (ret < 0) {
 470                dev_err(&info->pdev->dev, "%s:read err:%d\n", __func__, ret);
 471                ret = 0x30;
 472        }
 473
 474        /*
 475         * temperature is proportional to NTS thermistor resistance
 476         * ADC_RATE[5-4] determines current, 00=20uA,01=40uA,10=60uA,11=80uA
 477         * R_NTC(Ω) = [12-bit ADC VAL] * 800 / current
 478         */
 479        r = adc_val * 800 / (20 + (20 * ((ret >> 4) & 0x3)));
 480
 481        if (r < info->pdata->thermistor_curve[0][0]) {
 482                tval = info->pdata->thermistor_curve[0][1];
 483        } else if (r >= info->pdata->thermistor_curve[tcsz-1][0]) {
 484                tval = info->pdata->thermistor_curve[tcsz-1][1];
 485        } else {
 486                for (i = 1; i < tcsz; i++) {
 487                        if (r < info->pdata->thermistor_curve[i][0]) {
 488                                rmin = info->pdata->thermistor_curve[i-1][0];
 489                                rmax = info->pdata->thermistor_curve[i][0];
 490                                tmin = info->pdata->thermistor_curve[i-1][1];
 491                                tmax = info->pdata->thermistor_curve[i][1];
 492                                tval = tmin + ((tmax - tmin) *
 493                                        (r - rmin) / (rmax - rmin));
 494                                break;
 495                        }
 496                }
 497        }
 498
 499        return tval;
 500}
 501
 502static int fuel_gauge_get_btemp(struct axp288_fg_info *info, int *btemp)
 503{
 504        int ret, raw_val = 0;
 505
 506        ret = pmic_read_adc_val("axp288-batt-temp", &raw_val, info);
 507        if (ret < 0)
 508                goto temp_read_fail;
 509
 510        *btemp = adc_to_temp(info, raw_val);
 511
 512temp_read_fail:
 513        return ret;
 514}
 515
 516static int fuel_gauge_get_vocv(struct axp288_fg_info *info, int *vocv)
 517{
 518        int ret, value;
 519
 520        /* 12-bit data value, upper 8 in OCVH, lower 4 in OCVL */
 521        ret = fuel_gauge_reg_readb(info, AXP288_FG_OCVH_REG);
 522        if (ret < 0)
 523                goto vocv_read_fail;
 524        value = ret << 4;
 525
 526        ret = fuel_gauge_reg_readb(info, AXP288_FG_OCVL_REG);
 527        if (ret < 0)
 528                goto vocv_read_fail;
 529        value |= (ret & 0xf);
 530
 531        *vocv = VOLTAGE_FROM_ADC(value);
 532vocv_read_fail:
 533        return ret;
 534}
 535
 536static int fuel_gauge_battery_health(struct axp288_fg_info *info)
 537{
 538        int temp, vocv;
 539        int ret, health = POWER_SUPPLY_HEALTH_UNKNOWN;
 540
 541        ret = fuel_gauge_get_btemp(info, &temp);
 542        if (ret < 0)
 543                goto health_read_fail;
 544
 545        ret = fuel_gauge_get_vocv(info, &vocv);
 546        if (ret < 0)
 547                goto health_read_fail;
 548
 549        if (vocv > info->pdata->max_volt)
 550                health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
 551        else if (temp > info->pdata->max_temp)
 552                health = POWER_SUPPLY_HEALTH_OVERHEAT;
 553        else if (temp < info->pdata->min_temp)
 554                health = POWER_SUPPLY_HEALTH_COLD;
 555        else if (vocv < info->pdata->min_volt)
 556                health = POWER_SUPPLY_HEALTH_DEAD;
 557        else
 558                health = POWER_SUPPLY_HEALTH_GOOD;
 559
 560health_read_fail:
 561        return health;
 562}
 563
 564static int fuel_gauge_set_high_btemp_alert(struct axp288_fg_info *info)
 565{
 566        int ret, adc_val;
 567
 568        /* program temperature threshold as 1/16 ADC value */
 569        adc_val = temp_to_adc(info, info->pdata->max_temp);
 570        ret = fuel_gauge_reg_writeb(info, AXP20X_V_HTF_DISCHRG, adc_val >> 4);
 571
 572        return ret;
 573}
 574
 575static int fuel_gauge_set_low_btemp_alert(struct axp288_fg_info *info)
 576{
 577        int ret, adc_val;
 578
 579        /* program temperature threshold as 1/16 ADC value */
 580        adc_val = temp_to_adc(info, info->pdata->min_temp);
 581        ret = fuel_gauge_reg_writeb(info, AXP20X_V_LTF_DISCHRG, adc_val >> 4);
 582
 583        return ret;
 584}
 585
 586static int fuel_gauge_get_property(struct power_supply *ps,
 587                enum power_supply_property prop,
 588                union power_supply_propval *val)
 589{
 590        struct axp288_fg_info *info = power_supply_get_drvdata(ps);
 591        int ret = 0, value;
 592
 593        mutex_lock(&info->lock);
 594        switch (prop) {
 595        case POWER_SUPPLY_PROP_STATUS:
 596                fuel_gauge_get_status(info);
 597                val->intval = info->status;
 598                break;
 599        case POWER_SUPPLY_PROP_HEALTH:
 600                val->intval = fuel_gauge_battery_health(info);
 601                break;
 602        case POWER_SUPPLY_PROP_VOLTAGE_NOW:
 603                ret = fuel_gauge_get_vbatt(info, &value);
 604                if (ret < 0)
 605                        goto fuel_gauge_read_err;
 606                val->intval = PROP_VOLT(value);
 607                break;
 608        case POWER_SUPPLY_PROP_VOLTAGE_OCV:
 609                ret = fuel_gauge_get_vocv(info, &value);
 610                if (ret < 0)
 611                        goto fuel_gauge_read_err;
 612                val->intval = PROP_VOLT(value);
 613                break;
 614        case POWER_SUPPLY_PROP_CURRENT_NOW:
 615                ret = fuel_gauge_get_current(info, &value);
 616                if (ret < 0)
 617                        goto fuel_gauge_read_err;
 618                val->intval = PROP_CURR(value);
 619                break;
 620        case POWER_SUPPLY_PROP_PRESENT:
 621                ret = fuel_gauge_reg_readb(info, AXP20X_PWR_OP_MODE);
 622                if (ret < 0)
 623                        goto fuel_gauge_read_err;
 624
 625                if (ret & CHRG_STAT_BAT_PRESENT)
 626                        val->intval = 1;
 627                else
 628                        val->intval = 0;
 629                break;
 630        case POWER_SUPPLY_PROP_CAPACITY:
 631                ret = fuel_gauge_reg_readb(info, AXP20X_FG_RES);
 632                if (ret < 0)
 633                        goto fuel_gauge_read_err;
 634
 635                if (!(ret & FG_REP_CAP_VALID))
 636                        dev_err(&info->pdev->dev,
 637                                "capacity measurement not valid\n");
 638                val->intval = (ret & FG_REP_CAP_VAL_MASK);
 639                break;
 640        case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN:
 641                ret = fuel_gauge_reg_readb(info, AXP288_FG_LOW_CAP_REG);
 642                if (ret < 0)
 643                        goto fuel_gauge_read_err;
 644                val->intval = (ret & 0x0f);
 645                break;
 646        case POWER_SUPPLY_PROP_TEMP:
 647                ret = fuel_gauge_get_btemp(info, &value);
 648                if (ret < 0)
 649                        goto fuel_gauge_read_err;
 650                val->intval = PROP_TEMP(value);
 651                break;
 652        case POWER_SUPPLY_PROP_TEMP_MAX:
 653        case POWER_SUPPLY_PROP_TEMP_ALERT_MAX:
 654                val->intval = PROP_TEMP(info->pdata->max_temp);
 655                break;
 656        case POWER_SUPPLY_PROP_TEMP_MIN:
 657        case POWER_SUPPLY_PROP_TEMP_ALERT_MIN:
 658                val->intval = PROP_TEMP(info->pdata->min_temp);
 659                break;
 660        case POWER_SUPPLY_PROP_TECHNOLOGY:
 661                val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
 662                break;
 663        case POWER_SUPPLY_PROP_CHARGE_NOW:
 664                ret = fuel_gauge_reg_readb(info, AXP288_FG_CC_MTR1_REG);
 665                if (ret < 0)
 666                        goto fuel_gauge_read_err;
 667
 668                value = (ret & FG_CC_MTR1_VAL_MASK) << 8;
 669                ret = fuel_gauge_reg_readb(info, AXP288_FG_CC_MTR0_REG);
 670                if (ret < 0)
 671                        goto fuel_gauge_read_err;
 672                value |= (ret & FG_CC_MTR0_VAL_MASK);
 673                val->intval = value * FG_DES_CAP_RES_LSB;
 674                break;
 675        case POWER_SUPPLY_PROP_CHARGE_FULL:
 676                ret = fuel_gauge_reg_readb(info, AXP288_FG_DES_CAP1_REG);
 677                if (ret < 0)
 678                        goto fuel_gauge_read_err;
 679
 680                value = (ret & FG_DES_CAP1_VAL_MASK) << 8;
 681                ret = fuel_gauge_reg_readb(info, AXP288_FG_DES_CAP0_REG);
 682                if (ret < 0)
 683                        goto fuel_gauge_read_err;
 684                value |= (ret & FG_DES_CAP0_VAL_MASK);
 685                val->intval = value * FG_DES_CAP_RES_LSB;
 686                break;
 687        case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
 688                val->intval = PROP_CURR(info->pdata->design_cap);
 689                break;
 690        case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
 691                val->intval = PROP_VOLT(info->pdata->max_volt);
 692                break;
 693        case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
 694                val->intval = PROP_VOLT(info->pdata->min_volt);
 695                break;
 696        case POWER_SUPPLY_PROP_MODEL_NAME:
 697                val->strval = info->pdata->battid;
 698                break;
 699        default:
 700                mutex_unlock(&info->lock);
 701                return -EINVAL;
 702        }
 703
 704        mutex_unlock(&info->lock);
 705        return 0;
 706
 707fuel_gauge_read_err:
 708        mutex_unlock(&info->lock);
 709        return ret;
 710}
 711
 712static int fuel_gauge_set_property(struct power_supply *ps,
 713                enum power_supply_property prop,
 714                const union power_supply_propval *val)
 715{
 716        struct axp288_fg_info *info = power_supply_get_drvdata(ps);
 717        int ret = 0;
 718
 719        mutex_lock(&info->lock);
 720        switch (prop) {
 721        case POWER_SUPPLY_PROP_STATUS:
 722                info->status = val->intval;
 723                break;
 724        case POWER_SUPPLY_PROP_TEMP_MIN:
 725        case POWER_SUPPLY_PROP_TEMP_ALERT_MIN:
 726                if ((val->intval < PD_DEF_MIN_TEMP) ||
 727                        (val->intval > PD_DEF_MAX_TEMP)) {
 728                        ret = -EINVAL;
 729                        break;
 730                }
 731                info->pdata->min_temp = UNPROP_TEMP(val->intval);
 732                ret = fuel_gauge_set_low_btemp_alert(info);
 733                if (ret < 0)
 734                        dev_err(&info->pdev->dev,
 735                                "temp alert min set fail:%d\n", ret);
 736                break;
 737        case POWER_SUPPLY_PROP_TEMP_MAX:
 738        case POWER_SUPPLY_PROP_TEMP_ALERT_MAX:
 739                if ((val->intval < PD_DEF_MIN_TEMP) ||
 740                        (val->intval > PD_DEF_MAX_TEMP)) {
 741                        ret = -EINVAL;
 742                        break;
 743                }
 744                info->pdata->max_temp = UNPROP_TEMP(val->intval);
 745                ret = fuel_gauge_set_high_btemp_alert(info);
 746                if (ret < 0)
 747                        dev_err(&info->pdev->dev,
 748                                "temp alert max set fail:%d\n", ret);
 749                break;
 750        case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN:
 751                if ((val->intval < 0) || (val->intval > 15)) {
 752                        ret = -EINVAL;
 753                        break;
 754                }
 755                ret = fuel_gauge_reg_readb(info, AXP288_FG_LOW_CAP_REG);
 756                if (ret < 0)
 757                        break;
 758                ret &= 0xf0;
 759                ret |= (val->intval & 0xf);
 760                ret = fuel_gauge_reg_writeb(info, AXP288_FG_LOW_CAP_REG, ret);
 761                break;
 762        default:
 763                ret = -EINVAL;
 764                break;
 765        }
 766
 767        mutex_unlock(&info->lock);
 768        return ret;
 769}
 770
 771static int fuel_gauge_property_is_writeable(struct power_supply *psy,
 772        enum power_supply_property psp)
 773{
 774        int ret;
 775
 776        switch (psp) {
 777        case POWER_SUPPLY_PROP_STATUS:
 778        case POWER_SUPPLY_PROP_TEMP_MIN:
 779        case POWER_SUPPLY_PROP_TEMP_ALERT_MIN:
 780        case POWER_SUPPLY_PROP_TEMP_MAX:
 781        case POWER_SUPPLY_PROP_TEMP_ALERT_MAX:
 782        case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN:
 783                ret = 1;
 784                break;
 785        default:
 786                ret = 0;
 787        }
 788
 789        return ret;
 790}
 791
 792static void fuel_gauge_status_monitor(struct work_struct *work)
 793{
 794        struct axp288_fg_info *info = container_of(work,
 795                struct axp288_fg_info, status_monitor.work);
 796
 797        fuel_gauge_get_status(info);
 798        power_supply_changed(info->bat);
 799        schedule_delayed_work(&info->status_monitor, STATUS_MON_DELAY_JIFFIES);
 800}
 801
 802static irqreturn_t fuel_gauge_thread_handler(int irq, void *dev)
 803{
 804        struct axp288_fg_info *info = dev;
 805        int i;
 806
 807        for (i = 0; i < AXP288_FG_INTR_NUM; i++) {
 808                if (info->irq[i] == irq)
 809                        break;
 810        }
 811
 812        if (i >= AXP288_FG_INTR_NUM) {
 813                dev_warn(&info->pdev->dev, "spurious interrupt!!\n");
 814                return IRQ_NONE;
 815        }
 816
 817        switch (i) {
 818        case QWBTU_IRQ:
 819                dev_info(&info->pdev->dev,
 820                        "Quit Battery under temperature in work mode IRQ (QWBTU)\n");
 821                break;
 822        case WBTU_IRQ:
 823                dev_info(&info->pdev->dev,
 824                        "Battery under temperature in work mode IRQ (WBTU)\n");
 825                break;
 826        case QWBTO_IRQ:
 827                dev_info(&info->pdev->dev,
 828                        "Quit Battery over temperature in work mode IRQ (QWBTO)\n");
 829                break;
 830        case WBTO_IRQ:
 831                dev_info(&info->pdev->dev,
 832                        "Battery over temperature in work mode IRQ (WBTO)\n");
 833                break;
 834        case WL2_IRQ:
 835                dev_info(&info->pdev->dev, "Low Batt Warning(2) INTR\n");
 836                break;
 837        case WL1_IRQ:
 838                dev_info(&info->pdev->dev, "Low Batt Warning(1) INTR\n");
 839                break;
 840        default:
 841                dev_warn(&info->pdev->dev, "Spurious Interrupt!!!\n");
 842        }
 843
 844        power_supply_changed(info->bat);
 845        return IRQ_HANDLED;
 846}
 847
 848static void fuel_gauge_external_power_changed(struct power_supply *psy)
 849{
 850        struct axp288_fg_info *info = power_supply_get_drvdata(psy);
 851
 852        power_supply_changed(info->bat);
 853}
 854
 855static const struct power_supply_desc fuel_gauge_desc = {
 856        .name                   = DEV_NAME,
 857        .type                   = POWER_SUPPLY_TYPE_BATTERY,
 858        .properties             = fuel_gauge_props,
 859        .num_properties         = ARRAY_SIZE(fuel_gauge_props),
 860        .get_property           = fuel_gauge_get_property,
 861        .set_property           = fuel_gauge_set_property,
 862        .property_is_writeable  = fuel_gauge_property_is_writeable,
 863        .external_power_changed = fuel_gauge_external_power_changed,
 864};
 865
 866static int fuel_gauge_set_lowbatt_thresholds(struct axp288_fg_info *info)
 867{
 868        int ret;
 869        u8 reg_val;
 870
 871        ret = fuel_gauge_reg_readb(info, AXP20X_FG_RES);
 872        if (ret < 0) {
 873                dev_err(&info->pdev->dev, "%s:read err:%d\n", __func__, ret);
 874                return ret;
 875        }
 876        ret = (ret & FG_REP_CAP_VAL_MASK);
 877
 878        if (ret > FG_LOW_CAP_WARN_THR)
 879                reg_val = FG_LOW_CAP_WARN_THR;
 880        else if (ret > FG_LOW_CAP_CRIT_THR)
 881                reg_val = FG_LOW_CAP_CRIT_THR;
 882        else
 883                reg_val = FG_LOW_CAP_SHDN_THR;
 884
 885        reg_val |= FG_LOW_CAP_THR1_VAL;
 886        ret = fuel_gauge_reg_writeb(info, AXP288_FG_LOW_CAP_REG, reg_val);
 887        if (ret < 0)
 888                dev_err(&info->pdev->dev, "%s:write err:%d\n", __func__, ret);
 889
 890        return ret;
 891}
 892
 893static int fuel_gauge_program_vbatt_full(struct axp288_fg_info *info)
 894{
 895        int ret;
 896        u8 val;
 897
 898        ret = fuel_gauge_reg_readb(info, AXP20X_CHRG_CTRL1);
 899        if (ret < 0)
 900                goto fg_prog_ocv_fail;
 901        else
 902                val = (ret & ~CHRG_CCCV_CV_MASK);
 903
 904        switch (info->pdata->max_volt) {
 905        case CV_4100:
 906                val |= (CHRG_CCCV_CV_4100MV << CHRG_CCCV_CV_BIT_POS);
 907                break;
 908        case CV_4150:
 909                val |= (CHRG_CCCV_CV_4150MV << CHRG_CCCV_CV_BIT_POS);
 910                break;
 911        case CV_4200:
 912                val |= (CHRG_CCCV_CV_4200MV << CHRG_CCCV_CV_BIT_POS);
 913                break;
 914        case CV_4350:
 915                val |= (CHRG_CCCV_CV_4350MV << CHRG_CCCV_CV_BIT_POS);
 916                break;
 917        default:
 918                val |= (CHRG_CCCV_CV_4200MV << CHRG_CCCV_CV_BIT_POS);
 919                break;
 920        }
 921
 922        ret = fuel_gauge_reg_writeb(info, AXP20X_CHRG_CTRL1, val);
 923fg_prog_ocv_fail:
 924        return ret;
 925}
 926
 927static int fuel_gauge_program_design_cap(struct axp288_fg_info *info)
 928{
 929        int ret;
 930
 931        ret = fuel_gauge_reg_writeb(info,
 932                AXP288_FG_DES_CAP1_REG, info->pdata->cap1);
 933        if (ret < 0)
 934                goto fg_prog_descap_fail;
 935
 936        ret = fuel_gauge_reg_writeb(info,
 937                AXP288_FG_DES_CAP0_REG, info->pdata->cap0);
 938
 939fg_prog_descap_fail:
 940        return ret;
 941}
 942
 943static int fuel_gauge_program_ocv_curve(struct axp288_fg_info *info)
 944{
 945        int ret = 0, i;
 946
 947        for (i = 0; i < OCV_CURVE_SIZE; i++) {
 948                ret = fuel_gauge_reg_writeb(info,
 949                        AXP288_FG_OCV_CURVE_REG + i, info->pdata->ocv_curve[i]);
 950                if (ret < 0)
 951                        goto fg_prog_ocv_fail;
 952        }
 953
 954fg_prog_ocv_fail:
 955        return ret;
 956}
 957
 958static int fuel_gauge_program_rdc_vals(struct axp288_fg_info *info)
 959{
 960        int ret;
 961
 962        ret = fuel_gauge_reg_writeb(info,
 963                AXP288_FG_RDC1_REG, info->pdata->rdc1);
 964        if (ret < 0)
 965                goto fg_prog_ocv_fail;
 966
 967        ret = fuel_gauge_reg_writeb(info,
 968                AXP288_FG_RDC0_REG, info->pdata->rdc0);
 969
 970fg_prog_ocv_fail:
 971        return ret;
 972}
 973
 974static void fuel_gauge_init_config_regs(struct axp288_fg_info *info)
 975{
 976        int ret;
 977
 978        /*
 979         * check if the config data is already
 980         * programmed and if so just return.
 981         */
 982
 983        ret = fuel_gauge_reg_readb(info, AXP288_FG_DES_CAP1_REG);
 984        if (ret < 0) {
 985                dev_warn(&info->pdev->dev, "CAP1 reg read err!!\n");
 986        } else if (!(ret & FG_DES_CAP1_VALID)) {
 987                dev_info(&info->pdev->dev, "FG data needs to be initialized\n");
 988        } else {
 989                dev_info(&info->pdev->dev, "FG data is already initialized\n");
 990                return;
 991        }
 992
 993        ret = fuel_gauge_program_vbatt_full(info);
 994        if (ret < 0)
 995                dev_err(&info->pdev->dev, "set vbatt full fail:%d\n", ret);
 996
 997        ret = fuel_gauge_program_design_cap(info);
 998        if (ret < 0)
 999                dev_err(&info->pdev->dev, "set design cap fail:%d\n", ret);
1000
1001        ret = fuel_gauge_program_rdc_vals(info);
1002        if (ret < 0)
1003                dev_err(&info->pdev->dev, "set rdc fail:%d\n", ret);
1004
1005        ret = fuel_gauge_program_ocv_curve(info);
1006        if (ret < 0)
1007                dev_err(&info->pdev->dev, "set ocv curve fail:%d\n", ret);
1008
1009        ret = fuel_gauge_set_lowbatt_thresholds(info);
1010        if (ret < 0)
1011                dev_err(&info->pdev->dev, "lowbatt thr set fail:%d\n", ret);
1012
1013        ret = fuel_gauge_reg_writeb(info, AXP20X_CC_CTRL, 0xef);
1014        if (ret < 0)
1015                dev_err(&info->pdev->dev, "gauge cntl set fail:%d\n", ret);
1016}
1017
1018static void fuel_gauge_init_irq(struct axp288_fg_info *info)
1019{
1020        int ret, i, pirq;
1021
1022        for (i = 0; i < AXP288_FG_INTR_NUM; i++) {
1023                pirq = platform_get_irq(info->pdev, i);
1024                info->irq[i] = regmap_irq_get_virq(info->regmap_irqc, pirq);
1025                if (info->irq[i] < 0) {
1026                        dev_warn(&info->pdev->dev,
1027                                "regmap_irq get virq failed for IRQ %d: %d\n",
1028                                pirq, info->irq[i]);
1029                        info->irq[i] = -1;
1030                        goto intr_failed;
1031                }
1032                ret = request_threaded_irq(info->irq[i],
1033                                NULL, fuel_gauge_thread_handler,
1034                                IRQF_ONESHOT, DEV_NAME, info);
1035                if (ret) {
1036                        dev_warn(&info->pdev->dev,
1037                                "request irq failed for IRQ %d: %d\n",
1038                                pirq, info->irq[i]);
1039                        info->irq[i] = -1;
1040                        goto intr_failed;
1041                } else {
1042                        dev_info(&info->pdev->dev, "HW IRQ %d -> VIRQ %d\n",
1043                                pirq, info->irq[i]);
1044                }
1045        }
1046        return;
1047
1048intr_failed:
1049        for (; i > 0; i--) {
1050                free_irq(info->irq[i - 1], info);
1051                info->irq[i - 1] = -1;
1052        }
1053}
1054
1055static void fuel_gauge_init_hw_regs(struct axp288_fg_info *info)
1056{
1057        int ret;
1058        unsigned int val;
1059
1060        ret = fuel_gauge_set_high_btemp_alert(info);
1061        if (ret < 0)
1062                dev_err(&info->pdev->dev, "high batt temp set fail:%d\n", ret);
1063
1064        ret = fuel_gauge_set_low_btemp_alert(info);
1065        if (ret < 0)
1066                dev_err(&info->pdev->dev, "low batt temp set fail:%d\n", ret);
1067
1068        /* enable interrupts */
1069        val = fuel_gauge_reg_readb(info, AXP20X_IRQ3_EN);
1070        val |= TEMP_IRQ_CFG_MASK;
1071        fuel_gauge_reg_writeb(info, AXP20X_IRQ3_EN, val);
1072
1073        val = fuel_gauge_reg_readb(info, AXP20X_IRQ4_EN);
1074        val |= FG_IRQ_CFG_LOWBATT_MASK;
1075        val = fuel_gauge_reg_writeb(info, AXP20X_IRQ4_EN, val);
1076}
1077
1078static int axp288_fuel_gauge_probe(struct platform_device *pdev)
1079{
1080        int ret = 0;
1081        struct axp288_fg_info *info;
1082        struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
1083        struct power_supply_config psy_cfg = {};
1084
1085        info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
1086        if (!info)
1087                return -ENOMEM;
1088
1089        info->pdev = pdev;
1090        info->regmap = axp20x->regmap;
1091        info->regmap_irqc = axp20x->regmap_irqc;
1092        info->status = POWER_SUPPLY_STATUS_UNKNOWN;
1093        info->pdata = pdev->dev.platform_data;
1094        if (!info->pdata)
1095                return -ENODEV;
1096
1097        platform_set_drvdata(pdev, info);
1098
1099        mutex_init(&info->lock);
1100        INIT_DELAYED_WORK(&info->status_monitor, fuel_gauge_status_monitor);
1101
1102        psy_cfg.drv_data = info;
1103        info->bat = power_supply_register(&pdev->dev, &fuel_gauge_desc, &psy_cfg);
1104        if (IS_ERR(info->bat)) {
1105                ret = PTR_ERR(info->bat);
1106                dev_err(&pdev->dev, "failed to register battery: %d\n", ret);
1107                return ret;
1108        }
1109
1110        fuel_gauge_create_debugfs(info);
1111        fuel_gauge_init_config_regs(info);
1112        fuel_gauge_init_irq(info);
1113        fuel_gauge_init_hw_regs(info);
1114        schedule_delayed_work(&info->status_monitor, STATUS_MON_DELAY_JIFFIES);
1115
1116        return ret;
1117}
1118
1119static const struct platform_device_id axp288_fg_id_table[] = {
1120        { .name = DEV_NAME },
1121        {},
1122};
1123
1124static int axp288_fuel_gauge_remove(struct platform_device *pdev)
1125{
1126        struct axp288_fg_info *info = platform_get_drvdata(pdev);
1127        int i;
1128
1129        cancel_delayed_work_sync(&info->status_monitor);
1130        power_supply_unregister(info->bat);
1131        fuel_gauge_remove_debugfs(info);
1132
1133        for (i = 0; i < AXP288_FG_INTR_NUM; i++)
1134                if (info->irq[i] >= 0)
1135                        free_irq(info->irq[i], info);
1136
1137        return 0;
1138}
1139
1140static struct platform_driver axp288_fuel_gauge_driver = {
1141        .probe = axp288_fuel_gauge_probe,
1142        .remove = axp288_fuel_gauge_remove,
1143        .id_table = axp288_fg_id_table,
1144        .driver = {
1145                .name = DEV_NAME,
1146        },
1147};
1148
1149module_platform_driver(axp288_fuel_gauge_driver);
1150
1151MODULE_AUTHOR("Ramakrishna Pallala <ramakrishna.pallala@intel.com>");
1152MODULE_AUTHOR("Todd Brandt <todd.e.brandt@linux.intel.com>");
1153MODULE_DESCRIPTION("Xpower AXP288 Fuel Gauge Driver");
1154MODULE_LICENSE("GPL");
1155