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) 2016-2017 Hans de Goede <hdegoede@redhat.com>
   5 * Copyright (C) 2014 Intel Corporation
   6 *
   7 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   8 *
   9 * This program is free software; you can redistribute it and/or modify
  10 * it under the terms of the GNU General Public License as published by
  11 * the Free Software Foundation; version 2 of the License.
  12 *
  13 * This program is distributed in the hope that it will be useful, but
  14 * WITHOUT ANY WARRANTY; without even the implied warranty of
  15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  16 * General Public License for more details.
  17 *
  18 */
  19
  20#include <linux/dmi.h>
  21#include <linux/module.h>
  22#include <linux/kernel.h>
  23#include <linux/device.h>
  24#include <linux/regmap.h>
  25#include <linux/jiffies.h>
  26#include <linux/interrupt.h>
  27#include <linux/workqueue.h>
  28#include <linux/mfd/axp20x.h>
  29#include <linux/platform_device.h>
  30#include <linux/power_supply.h>
  31#include <linux/iio/consumer.h>
  32#include <linux/debugfs.h>
  33#include <linux/seq_file.h>
  34#include <asm/unaligned.h>
  35
  36#define PS_STAT_VBUS_TRIGGER            (1 << 0)
  37#define PS_STAT_BAT_CHRG_DIR            (1 << 2)
  38#define PS_STAT_VBAT_ABOVE_VHOLD        (1 << 3)
  39#define PS_STAT_VBUS_VALID              (1 << 4)
  40#define PS_STAT_VBUS_PRESENT            (1 << 5)
  41
  42#define CHRG_STAT_BAT_SAFE_MODE         (1 << 3)
  43#define CHRG_STAT_BAT_VALID                     (1 << 4)
  44#define CHRG_STAT_BAT_PRESENT           (1 << 5)
  45#define CHRG_STAT_CHARGING                      (1 << 6)
  46#define CHRG_STAT_PMIC_OTP                      (1 << 7)
  47
  48#define CHRG_CCCV_CC_MASK                       0xf     /* 4 bits */
  49#define CHRG_CCCV_CC_BIT_POS            0
  50#define CHRG_CCCV_CC_OFFSET                     200     /* 200mA */
  51#define CHRG_CCCV_CC_LSB_RES            200     /* 200mA */
  52#define CHRG_CCCV_ITERM_20P                     (1 << 4)    /* 20% of CC */
  53#define CHRG_CCCV_CV_MASK                       0x60        /* 2 bits */
  54#define CHRG_CCCV_CV_BIT_POS            5
  55#define CHRG_CCCV_CV_4100MV                     0x0     /* 4.10V */
  56#define CHRG_CCCV_CV_4150MV                     0x1     /* 4.15V */
  57#define CHRG_CCCV_CV_4200MV                     0x2     /* 4.20V */
  58#define CHRG_CCCV_CV_4350MV                     0x3     /* 4.35V */
  59#define CHRG_CCCV_CHG_EN                        (1 << 7)
  60
  61#define FG_CNTL_OCV_ADJ_STAT            (1 << 2)
  62#define FG_CNTL_OCV_ADJ_EN                      (1 << 3)
  63#define FG_CNTL_CAP_ADJ_STAT            (1 << 4)
  64#define FG_CNTL_CAP_ADJ_EN                      (1 << 5)
  65#define FG_CNTL_CC_EN                           (1 << 6)
  66#define FG_CNTL_GAUGE_EN                        (1 << 7)
  67
  68#define FG_15BIT_WORD_VALID                     (1 << 15)
  69#define FG_15BIT_VAL_MASK                       0x7fff
  70
  71#define FG_REP_CAP_VALID                        (1 << 7)
  72#define FG_REP_CAP_VAL_MASK                     0x7F
  73
  74#define FG_DES_CAP1_VALID                       (1 << 7)
  75#define FG_DES_CAP_RES_LSB                      1456    /* 1.456mAhr */
  76
  77#define FG_DES_CC_RES_LSB                       1456    /* 1.456mAhr */
  78
  79#define FG_OCV_CAP_VALID                        (1 << 7)
  80#define FG_OCV_CAP_VAL_MASK                     0x7F
  81#define FG_CC_CAP_VALID                         (1 << 7)
  82#define FG_CC_CAP_VAL_MASK                      0x7F
  83
  84#define FG_LOW_CAP_THR1_MASK            0xf0    /* 5% tp 20% */
  85#define FG_LOW_CAP_THR1_VAL                     0xa0    /* 15 perc */
  86#define FG_LOW_CAP_THR2_MASK            0x0f    /* 0% to 15% */
  87#define FG_LOW_CAP_WARN_THR                     14  /* 14 perc */
  88#define FG_LOW_CAP_CRIT_THR                     4   /* 4 perc */
  89#define FG_LOW_CAP_SHDN_THR                     0   /* 0 perc */
  90
  91#define STATUS_MON_DELAY_JIFFIES    (HZ * 60)   /*60 sec */
  92#define NR_RETRY_CNT    3
  93#define DEV_NAME        "axp288_fuel_gauge"
  94
  95/* 1.1mV per LSB expressed in uV */
  96#define VOLTAGE_FROM_ADC(a)                     ((a * 11) / 10)
  97/* properties converted to uV, uA */
  98#define PROP_VOLT(a)            ((a) * 1000)
  99#define PROP_CURR(a)            ((a) * 1000)
 100
 101#define AXP288_FG_INTR_NUM      6
 102enum {
 103        QWBTU_IRQ = 0,
 104        WBTU_IRQ,
 105        QWBTO_IRQ,
 106        WBTO_IRQ,
 107        WL2_IRQ,
 108        WL1_IRQ,
 109};
 110
 111enum {
 112        BAT_TEMP = 0,
 113        PMIC_TEMP,
 114        SYSTEM_TEMP,
 115        BAT_CHRG_CURR,
 116        BAT_D_CURR,
 117        BAT_VOLT,
 118        IIO_CHANNEL_NUM
 119};
 120
 121struct axp288_fg_info {
 122        struct platform_device *pdev;
 123        struct regmap *regmap;
 124        struct regmap_irq_chip_data *regmap_irqc;
 125        int irq[AXP288_FG_INTR_NUM];
 126        struct iio_channel *iio_channel[IIO_CHANNEL_NUM];
 127        struct power_supply *bat;
 128        struct mutex lock;
 129        int status;
 130        int max_volt;
 131        struct delayed_work status_monitor;
 132        struct dentry *debug_file;
 133};
 134
 135static enum power_supply_property fuel_gauge_props[] = {
 136        POWER_SUPPLY_PROP_STATUS,
 137        POWER_SUPPLY_PROP_PRESENT,
 138        POWER_SUPPLY_PROP_HEALTH,
 139        POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
 140        POWER_SUPPLY_PROP_VOLTAGE_NOW,
 141        POWER_SUPPLY_PROP_VOLTAGE_OCV,
 142        POWER_SUPPLY_PROP_CURRENT_NOW,
 143        POWER_SUPPLY_PROP_CAPACITY,
 144        POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN,
 145        POWER_SUPPLY_PROP_TECHNOLOGY,
 146        POWER_SUPPLY_PROP_CHARGE_FULL,
 147        POWER_SUPPLY_PROP_CHARGE_NOW,
 148};
 149
 150static int fuel_gauge_reg_readb(struct axp288_fg_info *info, int reg)
 151{
 152        int ret, i;
 153        unsigned int val;
 154
 155        for (i = 0; i < NR_RETRY_CNT; i++) {
 156                ret = regmap_read(info->regmap, reg, &val);
 157                if (ret == -EBUSY)
 158                        continue;
 159                else
 160                        break;
 161        }
 162
 163        if (ret < 0) {
 164                dev_err(&info->pdev->dev, "axp288 reg read err:%d\n", ret);
 165                return ret;
 166        }
 167
 168        return val;
 169}
 170
 171static int fuel_gauge_reg_writeb(struct axp288_fg_info *info, int reg, u8 val)
 172{
 173        int ret;
 174
 175        ret = regmap_write(info->regmap, reg, (unsigned int)val);
 176
 177        if (ret < 0)
 178                dev_err(&info->pdev->dev, "axp288 reg write err:%d\n", ret);
 179
 180        return ret;
 181}
 182
 183static int fuel_gauge_read_15bit_word(struct axp288_fg_info *info, int reg)
 184{
 185        unsigned char buf[2];
 186        int ret;
 187
 188        ret = regmap_bulk_read(info->regmap, reg, buf, 2);
 189        if (ret < 0) {
 190                dev_err(&info->pdev->dev, "Error reading reg 0x%02x err: %d\n",
 191                        reg, ret);
 192                return ret;
 193        }
 194
 195        ret = get_unaligned_be16(buf);
 196        if (!(ret & FG_15BIT_WORD_VALID)) {
 197                dev_err(&info->pdev->dev, "Error reg 0x%02x contents not valid\n",
 198                        reg);
 199                return -ENXIO;
 200        }
 201
 202        return ret & FG_15BIT_VAL_MASK;
 203}
 204
 205static int fuel_gauge_read_12bit_word(struct axp288_fg_info *info, int reg)
 206{
 207        unsigned char buf[2];
 208        int ret;
 209
 210        ret = regmap_bulk_read(info->regmap, reg, buf, 2);
 211        if (ret < 0) {
 212                dev_err(&info->pdev->dev, "Error reading reg 0x%02x err: %d\n",
 213                        reg, ret);
 214                return ret;
 215        }
 216
 217        /* 12-bit data values have upper 8 bits in buf[0], lower 4 in buf[1] */
 218        return (buf[0] << 4) | ((buf[1] >> 4) & 0x0f);
 219}
 220
 221#ifdef CONFIG_DEBUG_FS
 222static int fuel_gauge_debug_show(struct seq_file *s, void *data)
 223{
 224        struct axp288_fg_info *info = s->private;
 225        int raw_val, ret;
 226
 227        seq_printf(s, " PWR_STATUS[%02x] : %02x\n",
 228                AXP20X_PWR_INPUT_STATUS,
 229                fuel_gauge_reg_readb(info, AXP20X_PWR_INPUT_STATUS));
 230        seq_printf(s, "PWR_OP_MODE[%02x] : %02x\n",
 231                AXP20X_PWR_OP_MODE,
 232                fuel_gauge_reg_readb(info, AXP20X_PWR_OP_MODE));
 233        seq_printf(s, " CHRG_CTRL1[%02x] : %02x\n",
 234                AXP20X_CHRG_CTRL1,
 235                fuel_gauge_reg_readb(info, AXP20X_CHRG_CTRL1));
 236        seq_printf(s, "       VLTF[%02x] : %02x\n",
 237                AXP20X_V_LTF_DISCHRG,
 238                fuel_gauge_reg_readb(info, AXP20X_V_LTF_DISCHRG));
 239        seq_printf(s, "       VHTF[%02x] : %02x\n",
 240                AXP20X_V_HTF_DISCHRG,
 241                fuel_gauge_reg_readb(info, AXP20X_V_HTF_DISCHRG));
 242        seq_printf(s, "    CC_CTRL[%02x] : %02x\n",
 243                AXP20X_CC_CTRL,
 244                fuel_gauge_reg_readb(info, AXP20X_CC_CTRL));
 245        seq_printf(s, "BATTERY CAP[%02x] : %02x\n",
 246                AXP20X_FG_RES,
 247                fuel_gauge_reg_readb(info, AXP20X_FG_RES));
 248        seq_printf(s, "    FG_RDC1[%02x] : %02x\n",
 249                AXP288_FG_RDC1_REG,
 250                fuel_gauge_reg_readb(info, AXP288_FG_RDC1_REG));
 251        seq_printf(s, "    FG_RDC0[%02x] : %02x\n",
 252                AXP288_FG_RDC0_REG,
 253                fuel_gauge_reg_readb(info, AXP288_FG_RDC0_REG));
 254        seq_printf(s, "     FG_OCV[%02x] : %04x\n",
 255                AXP288_FG_OCVH_REG,
 256                fuel_gauge_read_12bit_word(info, AXP288_FG_OCVH_REG));
 257        seq_printf(s, " FG_DES_CAP[%02x] : %04x\n",
 258                AXP288_FG_DES_CAP1_REG,
 259                fuel_gauge_read_15bit_word(info, AXP288_FG_DES_CAP1_REG));
 260        seq_printf(s, "  FG_CC_MTR[%02x] : %04x\n",
 261                AXP288_FG_CC_MTR1_REG,
 262                fuel_gauge_read_15bit_word(info, AXP288_FG_CC_MTR1_REG));
 263        seq_printf(s, " FG_OCV_CAP[%02x] : %02x\n",
 264                AXP288_FG_OCV_CAP_REG,
 265                fuel_gauge_reg_readb(info, AXP288_FG_OCV_CAP_REG));
 266        seq_printf(s, "  FG_CC_CAP[%02x] : %02x\n",
 267                AXP288_FG_CC_CAP_REG,
 268                fuel_gauge_reg_readb(info, AXP288_FG_CC_CAP_REG));
 269        seq_printf(s, " FG_LOW_CAP[%02x] : %02x\n",
 270                AXP288_FG_LOW_CAP_REG,
 271                fuel_gauge_reg_readb(info, AXP288_FG_LOW_CAP_REG));
 272        seq_printf(s, "TUNING_CTL0[%02x] : %02x\n",
 273                AXP288_FG_TUNE0,
 274                fuel_gauge_reg_readb(info, AXP288_FG_TUNE0));
 275        seq_printf(s, "TUNING_CTL1[%02x] : %02x\n",
 276                AXP288_FG_TUNE1,
 277                fuel_gauge_reg_readb(info, AXP288_FG_TUNE1));
 278        seq_printf(s, "TUNING_CTL2[%02x] : %02x\n",
 279                AXP288_FG_TUNE2,
 280                fuel_gauge_reg_readb(info, AXP288_FG_TUNE2));
 281        seq_printf(s, "TUNING_CTL3[%02x] : %02x\n",
 282                AXP288_FG_TUNE3,
 283                fuel_gauge_reg_readb(info, AXP288_FG_TUNE3));
 284        seq_printf(s, "TUNING_CTL4[%02x] : %02x\n",
 285                AXP288_FG_TUNE4,
 286                fuel_gauge_reg_readb(info, AXP288_FG_TUNE4));
 287        seq_printf(s, "TUNING_CTL5[%02x] : %02x\n",
 288                AXP288_FG_TUNE5,
 289                fuel_gauge_reg_readb(info, AXP288_FG_TUNE5));
 290
 291        ret = iio_read_channel_raw(info->iio_channel[BAT_TEMP], &raw_val);
 292        if (ret >= 0)
 293                seq_printf(s, "axp288-batttemp : %d\n", raw_val);
 294        ret = iio_read_channel_raw(info->iio_channel[PMIC_TEMP], &raw_val);
 295        if (ret >= 0)
 296                seq_printf(s, "axp288-pmictemp : %d\n", raw_val);
 297        ret = iio_read_channel_raw(info->iio_channel[SYSTEM_TEMP], &raw_val);
 298        if (ret >= 0)
 299                seq_printf(s, "axp288-systtemp : %d\n", raw_val);
 300        ret = iio_read_channel_raw(info->iio_channel[BAT_CHRG_CURR], &raw_val);
 301        if (ret >= 0)
 302                seq_printf(s, "axp288-chrgcurr : %d\n", raw_val);
 303        ret = iio_read_channel_raw(info->iio_channel[BAT_D_CURR], &raw_val);
 304        if (ret >= 0)
 305                seq_printf(s, "axp288-dchrgcur : %d\n", raw_val);
 306        ret = iio_read_channel_raw(info->iio_channel[BAT_VOLT], &raw_val);
 307        if (ret >= 0)
 308                seq_printf(s, "axp288-battvolt : %d\n", raw_val);
 309
 310        return 0;
 311}
 312
 313static int debug_open(struct inode *inode, struct file *file)
 314{
 315        return single_open(file, fuel_gauge_debug_show, inode->i_private);
 316}
 317
 318static const struct file_operations fg_debug_fops = {
 319        .open       = debug_open,
 320        .read       = seq_read,
 321        .llseek     = seq_lseek,
 322        .release    = single_release,
 323};
 324
 325static void fuel_gauge_create_debugfs(struct axp288_fg_info *info)
 326{
 327        info->debug_file = debugfs_create_file("fuelgauge", 0666, NULL,
 328                info, &fg_debug_fops);
 329}
 330
 331static void fuel_gauge_remove_debugfs(struct axp288_fg_info *info)
 332{
 333        debugfs_remove(info->debug_file);
 334}
 335#else
 336static inline void fuel_gauge_create_debugfs(struct axp288_fg_info *info)
 337{
 338}
 339static inline void fuel_gauge_remove_debugfs(struct axp288_fg_info *info)
 340{
 341}
 342#endif
 343
 344static void fuel_gauge_get_status(struct axp288_fg_info *info)
 345{
 346        int pwr_stat, fg_res, curr, ret;
 347
 348        pwr_stat = fuel_gauge_reg_readb(info, AXP20X_PWR_INPUT_STATUS);
 349        if (pwr_stat < 0) {
 350                dev_err(&info->pdev->dev,
 351                        "PWR STAT read failed:%d\n", pwr_stat);
 352                return;
 353        }
 354
 355        /* Report full if Vbus is valid and the reported capacity is 100% */
 356        if (!(pwr_stat & PS_STAT_VBUS_VALID))
 357                goto not_full;
 358
 359        fg_res = fuel_gauge_reg_readb(info, AXP20X_FG_RES);
 360        if (fg_res < 0) {
 361                dev_err(&info->pdev->dev, "FG RES read failed: %d\n", fg_res);
 362                return;
 363        }
 364        if (!(fg_res & FG_REP_CAP_VALID))
 365                goto not_full;
 366
 367        fg_res &= ~FG_REP_CAP_VALID;
 368        if (fg_res == 100) {
 369                info->status = POWER_SUPPLY_STATUS_FULL;
 370                return;
 371        }
 372
 373        /*
 374         * Sometimes the charger turns itself off before fg-res reaches 100%.
 375         * When this happens the AXP288 reports a not-charging status and
 376         * 0 mA discharge current.
 377         */
 378        if (fg_res < 90 || (pwr_stat & PS_STAT_BAT_CHRG_DIR))
 379                goto not_full;
 380
 381        ret = iio_read_channel_raw(info->iio_channel[BAT_D_CURR], &curr);
 382        if (ret < 0) {
 383                dev_err(&info->pdev->dev, "FG get current failed: %d\n", ret);
 384                return;
 385        }
 386        if (curr == 0) {
 387                info->status = POWER_SUPPLY_STATUS_FULL;
 388                return;
 389        }
 390
 391not_full:
 392        if (pwr_stat & PS_STAT_BAT_CHRG_DIR)
 393                info->status = POWER_SUPPLY_STATUS_CHARGING;
 394        else
 395                info->status = POWER_SUPPLY_STATUS_DISCHARGING;
 396}
 397
 398static int fuel_gauge_get_vbatt(struct axp288_fg_info *info, int *vbatt)
 399{
 400        int ret = 0, raw_val;
 401
 402        ret = iio_read_channel_raw(info->iio_channel[BAT_VOLT], &raw_val);
 403        if (ret < 0)
 404                goto vbatt_read_fail;
 405
 406        *vbatt = VOLTAGE_FROM_ADC(raw_val);
 407vbatt_read_fail:
 408        return ret;
 409}
 410
 411static int fuel_gauge_get_current(struct axp288_fg_info *info, int *cur)
 412{
 413        int ret, discharge;
 414
 415        /* First check discharge current, so that we do only 1 read on bat. */
 416        ret = iio_read_channel_raw(info->iio_channel[BAT_D_CURR], &discharge);
 417        if (ret < 0)
 418                return ret;
 419
 420        if (discharge > 0) {
 421                *cur = -1 * discharge;
 422                return 0;
 423        }
 424
 425        return iio_read_channel_raw(info->iio_channel[BAT_CHRG_CURR], cur);
 426}
 427
 428static int fuel_gauge_get_vocv(struct axp288_fg_info *info, int *vocv)
 429{
 430        int ret;
 431
 432        ret = fuel_gauge_read_12bit_word(info, AXP288_FG_OCVH_REG);
 433        if (ret >= 0)
 434                *vocv = VOLTAGE_FROM_ADC(ret);
 435
 436        return ret;
 437}
 438
 439static int fuel_gauge_battery_health(struct axp288_fg_info *info)
 440{
 441        int ret, vocv, health = POWER_SUPPLY_HEALTH_UNKNOWN;
 442
 443        ret = fuel_gauge_get_vocv(info, &vocv);
 444        if (ret < 0)
 445                goto health_read_fail;
 446
 447        if (vocv > info->max_volt)
 448                health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
 449        else
 450                health = POWER_SUPPLY_HEALTH_GOOD;
 451
 452health_read_fail:
 453        return health;
 454}
 455
 456static int fuel_gauge_get_property(struct power_supply *ps,
 457                enum power_supply_property prop,
 458                union power_supply_propval *val)
 459{
 460        struct axp288_fg_info *info = power_supply_get_drvdata(ps);
 461        int ret = 0, value;
 462
 463        mutex_lock(&info->lock);
 464        switch (prop) {
 465        case POWER_SUPPLY_PROP_STATUS:
 466                fuel_gauge_get_status(info);
 467                val->intval = info->status;
 468                break;
 469        case POWER_SUPPLY_PROP_HEALTH:
 470                val->intval = fuel_gauge_battery_health(info);
 471                break;
 472        case POWER_SUPPLY_PROP_VOLTAGE_NOW:
 473                ret = fuel_gauge_get_vbatt(info, &value);
 474                if (ret < 0)
 475                        goto fuel_gauge_read_err;
 476                val->intval = PROP_VOLT(value);
 477                break;
 478        case POWER_SUPPLY_PROP_VOLTAGE_OCV:
 479                ret = fuel_gauge_get_vocv(info, &value);
 480                if (ret < 0)
 481                        goto fuel_gauge_read_err;
 482                val->intval = PROP_VOLT(value);
 483                break;
 484        case POWER_SUPPLY_PROP_CURRENT_NOW:
 485                ret = fuel_gauge_get_current(info, &value);
 486                if (ret < 0)
 487                        goto fuel_gauge_read_err;
 488                val->intval = PROP_CURR(value);
 489                break;
 490        case POWER_SUPPLY_PROP_PRESENT:
 491                ret = fuel_gauge_reg_readb(info, AXP20X_PWR_OP_MODE);
 492                if (ret < 0)
 493                        goto fuel_gauge_read_err;
 494
 495                if (ret & CHRG_STAT_BAT_PRESENT)
 496                        val->intval = 1;
 497                else
 498                        val->intval = 0;
 499                break;
 500        case POWER_SUPPLY_PROP_CAPACITY:
 501                ret = fuel_gauge_reg_readb(info, AXP20X_FG_RES);
 502                if (ret < 0)
 503                        goto fuel_gauge_read_err;
 504
 505                if (!(ret & FG_REP_CAP_VALID))
 506                        dev_err(&info->pdev->dev,
 507                                "capacity measurement not valid\n");
 508                val->intval = (ret & FG_REP_CAP_VAL_MASK);
 509                break;
 510        case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN:
 511                ret = fuel_gauge_reg_readb(info, AXP288_FG_LOW_CAP_REG);
 512                if (ret < 0)
 513                        goto fuel_gauge_read_err;
 514                val->intval = (ret & 0x0f);
 515                break;
 516        case POWER_SUPPLY_PROP_TECHNOLOGY:
 517                val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
 518                break;
 519        case POWER_SUPPLY_PROP_CHARGE_NOW:
 520                ret = fuel_gauge_read_15bit_word(info, AXP288_FG_CC_MTR1_REG);
 521                if (ret < 0)
 522                        goto fuel_gauge_read_err;
 523
 524                val->intval = ret * FG_DES_CAP_RES_LSB;
 525                break;
 526        case POWER_SUPPLY_PROP_CHARGE_FULL:
 527                ret = fuel_gauge_read_15bit_word(info, AXP288_FG_DES_CAP1_REG);
 528                if (ret < 0)
 529                        goto fuel_gauge_read_err;
 530
 531                val->intval = ret * FG_DES_CAP_RES_LSB;
 532                break;
 533        case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
 534                val->intval = PROP_VOLT(info->max_volt);
 535                break;
 536        default:
 537                mutex_unlock(&info->lock);
 538                return -EINVAL;
 539        }
 540
 541        mutex_unlock(&info->lock);
 542        return 0;
 543
 544fuel_gauge_read_err:
 545        mutex_unlock(&info->lock);
 546        return ret;
 547}
 548
 549static int fuel_gauge_set_property(struct power_supply *ps,
 550                enum power_supply_property prop,
 551                const union power_supply_propval *val)
 552{
 553        struct axp288_fg_info *info = power_supply_get_drvdata(ps);
 554        int ret = 0;
 555
 556        mutex_lock(&info->lock);
 557        switch (prop) {
 558        case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN:
 559                if ((val->intval < 0) || (val->intval > 15)) {
 560                        ret = -EINVAL;
 561                        break;
 562                }
 563                ret = fuel_gauge_reg_readb(info, AXP288_FG_LOW_CAP_REG);
 564                if (ret < 0)
 565                        break;
 566                ret &= 0xf0;
 567                ret |= (val->intval & 0xf);
 568                ret = fuel_gauge_reg_writeb(info, AXP288_FG_LOW_CAP_REG, ret);
 569                break;
 570        default:
 571                ret = -EINVAL;
 572                break;
 573        }
 574
 575        mutex_unlock(&info->lock);
 576        return ret;
 577}
 578
 579static int fuel_gauge_property_is_writeable(struct power_supply *psy,
 580        enum power_supply_property psp)
 581{
 582        int ret;
 583
 584        switch (psp) {
 585        case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN:
 586                ret = 1;
 587                break;
 588        default:
 589                ret = 0;
 590        }
 591
 592        return ret;
 593}
 594
 595static void fuel_gauge_status_monitor(struct work_struct *work)
 596{
 597        struct axp288_fg_info *info = container_of(work,
 598                struct axp288_fg_info, status_monitor.work);
 599
 600        fuel_gauge_get_status(info);
 601        power_supply_changed(info->bat);
 602        schedule_delayed_work(&info->status_monitor, STATUS_MON_DELAY_JIFFIES);
 603}
 604
 605static irqreturn_t fuel_gauge_thread_handler(int irq, void *dev)
 606{
 607        struct axp288_fg_info *info = dev;
 608        int i;
 609
 610        for (i = 0; i < AXP288_FG_INTR_NUM; i++) {
 611                if (info->irq[i] == irq)
 612                        break;
 613        }
 614
 615        if (i >= AXP288_FG_INTR_NUM) {
 616                dev_warn(&info->pdev->dev, "spurious interrupt!!\n");
 617                return IRQ_NONE;
 618        }
 619
 620        switch (i) {
 621        case QWBTU_IRQ:
 622                dev_info(&info->pdev->dev,
 623                        "Quit Battery under temperature in work mode IRQ (QWBTU)\n");
 624                break;
 625        case WBTU_IRQ:
 626                dev_info(&info->pdev->dev,
 627                        "Battery under temperature in work mode IRQ (WBTU)\n");
 628                break;
 629        case QWBTO_IRQ:
 630                dev_info(&info->pdev->dev,
 631                        "Quit Battery over temperature in work mode IRQ (QWBTO)\n");
 632                break;
 633        case WBTO_IRQ:
 634                dev_info(&info->pdev->dev,
 635                        "Battery over temperature in work mode IRQ (WBTO)\n");
 636                break;
 637        case WL2_IRQ:
 638                dev_info(&info->pdev->dev, "Low Batt Warning(2) INTR\n");
 639                break;
 640        case WL1_IRQ:
 641                dev_info(&info->pdev->dev, "Low Batt Warning(1) INTR\n");
 642                break;
 643        default:
 644                dev_warn(&info->pdev->dev, "Spurious Interrupt!!!\n");
 645        }
 646
 647        power_supply_changed(info->bat);
 648        return IRQ_HANDLED;
 649}
 650
 651static void fuel_gauge_external_power_changed(struct power_supply *psy)
 652{
 653        struct axp288_fg_info *info = power_supply_get_drvdata(psy);
 654
 655        power_supply_changed(info->bat);
 656}
 657
 658static const struct power_supply_desc fuel_gauge_desc = {
 659        .name                   = DEV_NAME,
 660        .type                   = POWER_SUPPLY_TYPE_BATTERY,
 661        .properties             = fuel_gauge_props,
 662        .num_properties         = ARRAY_SIZE(fuel_gauge_props),
 663        .get_property           = fuel_gauge_get_property,
 664        .set_property           = fuel_gauge_set_property,
 665        .property_is_writeable  = fuel_gauge_property_is_writeable,
 666        .external_power_changed = fuel_gauge_external_power_changed,
 667};
 668
 669static void fuel_gauge_init_irq(struct axp288_fg_info *info)
 670{
 671        int ret, i, pirq;
 672
 673        for (i = 0; i < AXP288_FG_INTR_NUM; i++) {
 674                pirq = platform_get_irq(info->pdev, i);
 675                info->irq[i] = regmap_irq_get_virq(info->regmap_irqc, pirq);
 676                if (info->irq[i] < 0) {
 677                        dev_warn(&info->pdev->dev,
 678                                "regmap_irq get virq failed for IRQ %d: %d\n",
 679                                pirq, info->irq[i]);
 680                        info->irq[i] = -1;
 681                        goto intr_failed;
 682                }
 683                ret = request_threaded_irq(info->irq[i],
 684                                NULL, fuel_gauge_thread_handler,
 685                                IRQF_ONESHOT, DEV_NAME, info);
 686                if (ret) {
 687                        dev_warn(&info->pdev->dev,
 688                                "request irq failed for IRQ %d: %d\n",
 689                                pirq, info->irq[i]);
 690                        info->irq[i] = -1;
 691                        goto intr_failed;
 692                } else {
 693                        dev_info(&info->pdev->dev, "HW IRQ %d -> VIRQ %d\n",
 694                                pirq, info->irq[i]);
 695                }
 696        }
 697        return;
 698
 699intr_failed:
 700        for (; i > 0; i--) {
 701                free_irq(info->irq[i - 1], info);
 702                info->irq[i - 1] = -1;
 703        }
 704}
 705
 706/*
 707 * Some devices have no battery (HDMI sticks) and the axp288 battery's
 708 * detection reports one despite it not being there.
 709 */
 710static const struct dmi_system_id axp288_fuel_gauge_blacklist[] = {
 711        {
 712                /* Intel Cherry Trail Compute Stick, Windows version */
 713                .matches = {
 714                        DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
 715                        DMI_MATCH(DMI_PRODUCT_NAME, "STK1AW32SC"),
 716                },
 717        },
 718        {
 719                /* Intel Cherry Trail Compute Stick, version without an OS */
 720                .matches = {
 721                        DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
 722                        DMI_MATCH(DMI_PRODUCT_NAME, "STK1A32SC"),
 723                },
 724        },
 725        {
 726                /* Meegopad T08 */
 727                .matches = {
 728                        DMI_MATCH(DMI_SYS_VENDOR, "Default string"),
 729                        DMI_MATCH(DMI_BOARD_VENDOR, "To be filled by OEM."),
 730                        DMI_MATCH(DMI_BOARD_NAME, "T3 MRD"),
 731                        DMI_MATCH(DMI_BOARD_VERSION, "V1.1"),
 732                },
 733        },
 734        {
 735                /* ECS EF20EA */
 736                .matches = {
 737                        DMI_MATCH(DMI_PRODUCT_NAME, "EF20EA"),
 738                },
 739        },
 740        {}
 741};
 742
 743static int axp288_fuel_gauge_probe(struct platform_device *pdev)
 744{
 745        int i, ret = 0;
 746        struct axp288_fg_info *info;
 747        struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
 748        struct power_supply_config psy_cfg = {};
 749        static const char * const iio_chan_name[] = {
 750                [BAT_TEMP] = "axp288-batt-temp",
 751                [PMIC_TEMP] = "axp288-pmic-temp",
 752                [SYSTEM_TEMP] = "axp288-system-temp",
 753                [BAT_CHRG_CURR] = "axp288-chrg-curr",
 754                [BAT_D_CURR] = "axp288-chrg-d-curr",
 755                [BAT_VOLT] = "axp288-batt-volt",
 756        };
 757
 758        if (dmi_check_system(axp288_fuel_gauge_blacklist))
 759                return -ENODEV;
 760
 761        info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
 762        if (!info)
 763                return -ENOMEM;
 764
 765        info->pdev = pdev;
 766        info->regmap = axp20x->regmap;
 767        info->regmap_irqc = axp20x->regmap_irqc;
 768        info->status = POWER_SUPPLY_STATUS_UNKNOWN;
 769
 770        platform_set_drvdata(pdev, info);
 771
 772        mutex_init(&info->lock);
 773        INIT_DELAYED_WORK(&info->status_monitor, fuel_gauge_status_monitor);
 774
 775        for (i = 0; i < IIO_CHANNEL_NUM; i++) {
 776                /*
 777                 * Note cannot use devm_iio_channel_get because x86 systems
 778                 * lack the device<->channel maps which iio_channel_get will
 779                 * try to use when passed a non NULL device pointer.
 780                 */
 781                info->iio_channel[i] =
 782                        iio_channel_get(NULL, iio_chan_name[i]);
 783                if (IS_ERR(info->iio_channel[i])) {
 784                        ret = PTR_ERR(info->iio_channel[i]);
 785                        dev_dbg(&pdev->dev, "error getting iiochan %s: %d\n",
 786                                iio_chan_name[i], ret);
 787                        /* Wait for axp288_adc to load */
 788                        if (ret == -ENODEV)
 789                                ret = -EPROBE_DEFER;
 790
 791                        goto out_free_iio_chan;
 792                }
 793        }
 794
 795        ret = fuel_gauge_reg_readb(info, AXP288_FG_DES_CAP1_REG);
 796        if (ret < 0)
 797                goto out_free_iio_chan;
 798
 799        if (!(ret & FG_DES_CAP1_VALID)) {
 800                dev_err(&pdev->dev, "axp288 not configured by firmware\n");
 801                ret = -ENODEV;
 802                goto out_free_iio_chan;
 803        }
 804
 805        ret = fuel_gauge_reg_readb(info, AXP20X_CHRG_CTRL1);
 806        if (ret < 0)
 807                goto out_free_iio_chan;
 808        switch ((ret & CHRG_CCCV_CV_MASK) >> CHRG_CCCV_CV_BIT_POS) {
 809        case CHRG_CCCV_CV_4100MV:
 810                info->max_volt = 4100;
 811                break;
 812        case CHRG_CCCV_CV_4150MV:
 813                info->max_volt = 4150;
 814                break;
 815        case CHRG_CCCV_CV_4200MV:
 816                info->max_volt = 4200;
 817                break;
 818        case CHRG_CCCV_CV_4350MV:
 819                info->max_volt = 4350;
 820                break;
 821        }
 822
 823        psy_cfg.drv_data = info;
 824        info->bat = power_supply_register(&pdev->dev, &fuel_gauge_desc, &psy_cfg);
 825        if (IS_ERR(info->bat)) {
 826                ret = PTR_ERR(info->bat);
 827                dev_err(&pdev->dev, "failed to register battery: %d\n", ret);
 828                goto out_free_iio_chan;
 829        }
 830
 831        fuel_gauge_create_debugfs(info);
 832        fuel_gauge_init_irq(info);
 833        schedule_delayed_work(&info->status_monitor, STATUS_MON_DELAY_JIFFIES);
 834
 835        return 0;
 836
 837out_free_iio_chan:
 838        for (i = 0; i < IIO_CHANNEL_NUM; i++)
 839                if (!IS_ERR_OR_NULL(info->iio_channel[i]))
 840                        iio_channel_release(info->iio_channel[i]);
 841
 842        return ret;
 843}
 844
 845static const struct platform_device_id axp288_fg_id_table[] = {
 846        { .name = DEV_NAME },
 847        {},
 848};
 849MODULE_DEVICE_TABLE(platform, axp288_fg_id_table);
 850
 851static int axp288_fuel_gauge_remove(struct platform_device *pdev)
 852{
 853        struct axp288_fg_info *info = platform_get_drvdata(pdev);
 854        int i;
 855
 856        cancel_delayed_work_sync(&info->status_monitor);
 857        power_supply_unregister(info->bat);
 858        fuel_gauge_remove_debugfs(info);
 859
 860        for (i = 0; i < AXP288_FG_INTR_NUM; i++)
 861                if (info->irq[i] >= 0)
 862                        free_irq(info->irq[i], info);
 863
 864        for (i = 0; i < IIO_CHANNEL_NUM; i++)
 865                iio_channel_release(info->iio_channel[i]);
 866
 867        return 0;
 868}
 869
 870static struct platform_driver axp288_fuel_gauge_driver = {
 871        .probe = axp288_fuel_gauge_probe,
 872        .remove = axp288_fuel_gauge_remove,
 873        .id_table = axp288_fg_id_table,
 874        .driver = {
 875                .name = DEV_NAME,
 876        },
 877};
 878
 879module_platform_driver(axp288_fuel_gauge_driver);
 880
 881MODULE_AUTHOR("Ramakrishna Pallala <ramakrishna.pallala@intel.com>");
 882MODULE_AUTHOR("Todd Brandt <todd.e.brandt@linux.intel.com>");
 883MODULE_DESCRIPTION("Xpower AXP288 Fuel Gauge Driver");
 884MODULE_LICENSE("GPL");
 885