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