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