1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * axp288_fuel_gauge.c - Xpower AXP288 PMIC Fuel Gauge Driver 4 * 5 * Copyright (C) 2020-2021 Andrejus Basovas <xxx@yyy.tld> 6 * Copyright (C) 2016-2021 Hans de Goede <hdegoede@redhat.com> 7 * Copyright (C) 2014 Intel Corporation 8 * 9 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 10 */ 11 12 #include <linux/dmi.h> 13 #include <linux/module.h> 14 #include <linux/kernel.h> 15 #include <linux/device.h> 16 #include <linux/regmap.h> 17 #include <linux/jiffies.h> 18 #include <linux/interrupt.h> 19 #include <linux/mfd/axp20x.h> 20 #include <linux/platform_device.h> 21 #include <linux/power_supply.h> 22 #include <linux/iio/consumer.h> 23 #include <asm/unaligned.h> 24 #include <asm/iosf_mbi.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 DEV_NAME "axp288_fuel_gauge" 82 83 /* 1.1mV per LSB expressed in uV */ 84 #define VOLTAGE_FROM_ADC(a) ((a * 11) / 10) 85 /* properties converted to uV, uA */ 86 #define PROP_VOLT(a) ((a) * 1000) 87 #define PROP_CURR(a) ((a) * 1000) 88 89 #define AXP288_REG_UPDATE_INTERVAL (60 * HZ) 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_CHRG_CURR, 102 BAT_D_CURR, 103 BAT_VOLT, 104 IIO_CHANNEL_NUM 105 }; 106 107 struct axp288_fg_info { 108 struct device *dev; 109 struct regmap *regmap; 110 struct regmap_irq_chip_data *regmap_irqc; 111 int irq[AXP288_FG_INTR_NUM]; 112 struct iio_channel *iio_channel[IIO_CHANNEL_NUM]; 113 struct power_supply *bat; 114 struct mutex lock; 115 int status; 116 int max_volt; 117 int pwr_op; 118 int low_cap; 119 struct dentry *debug_file; 120 121 char valid; /* zero until following fields are valid */ 122 unsigned long last_updated; /* in jiffies */ 123 124 int pwr_stat; 125 int fg_res; 126 int bat_volt; 127 int d_curr; 128 int c_curr; 129 int ocv; 130 int fg_cc_mtr1; 131 int fg_des_cap1; 132 }; 133 134 static enum power_supply_property fuel_gauge_props[] = { 135 POWER_SUPPLY_PROP_STATUS, 136 POWER_SUPPLY_PROP_PRESENT, 137 POWER_SUPPLY_PROP_HEALTH, 138 POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, 139 POWER_SUPPLY_PROP_VOLTAGE_NOW, 140 POWER_SUPPLY_PROP_VOLTAGE_OCV, 141 POWER_SUPPLY_PROP_CURRENT_NOW, 142 POWER_SUPPLY_PROP_CAPACITY, 143 POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN, 144 POWER_SUPPLY_PROP_TECHNOLOGY, 145 POWER_SUPPLY_PROP_CHARGE_FULL, 146 POWER_SUPPLY_PROP_CHARGE_NOW, 147 }; 148 149 static int fuel_gauge_reg_readb(struct axp288_fg_info *info, int reg) 150 { 151 unsigned int val; 152 int ret; 153 154 ret = regmap_read(info->regmap, reg, &val); 155 if (ret < 0) { 156 dev_err(info->dev, "Error reading reg 0x%02x err: %d\n", reg, ret); 157 return ret; 158 } 159 160 return val; 161 } 162 163 static int fuel_gauge_reg_writeb(struct axp288_fg_info *info, int reg, u8 val) 164 { 165 int ret; 166 167 ret = regmap_write(info->regmap, reg, (unsigned int)val); 168 169 if (ret < 0) 170 dev_err(info->dev, "Error writing reg 0x%02x err: %d\n", reg, ret); 171 172 return ret; 173 } 174 175 static int fuel_gauge_read_15bit_word(struct axp288_fg_info *info, int reg) 176 { 177 unsigned char buf[2]; 178 int ret; 179 180 ret = regmap_bulk_read(info->regmap, reg, buf, 2); 181 if (ret < 0) { 182 dev_err(info->dev, "Error reading reg 0x%02x err: %d\n", reg, ret); 183 return ret; 184 } 185 186 ret = get_unaligned_be16(buf); 187 if (!(ret & FG_15BIT_WORD_VALID)) { 188 dev_err(info->dev, "Error reg 0x%02x contents not valid\n", reg); 189 return -ENXIO; 190 } 191 192 return ret & FG_15BIT_VAL_MASK; 193 } 194 195 static int fuel_gauge_read_12bit_word(struct axp288_fg_info *info, int reg) 196 { 197 unsigned char buf[2]; 198 int ret; 199 200 ret = regmap_bulk_read(info->regmap, reg, buf, 2); 201 if (ret < 0) { 202 dev_err(info->dev, "Error reading reg 0x%02x err: %d\n", reg, ret); 203 return ret; 204 } 205 206 /* 12-bit data values have upper 8 bits in buf[0], lower 4 in buf[1] */ 207 return (buf[0] << 4) | ((buf[1] >> 4) & 0x0f); 208 } 209 210 static int fuel_gauge_update_registers(struct axp288_fg_info *info) 211 { 212 int ret; 213 214 if (info->valid && time_before(jiffies, info->last_updated + AXP288_REG_UPDATE_INTERVAL)) 215 return 0; 216 217 dev_dbg(info->dev, "Fuel Gauge updating register values...\n"); 218 219 ret = iosf_mbi_block_punit_i2c_access(); 220 if (ret < 0) 221 return ret; 222 223 ret = fuel_gauge_reg_readb(info, AXP20X_PWR_INPUT_STATUS); 224 if (ret < 0) 225 goto out; 226 info->pwr_stat = ret; 227 228 ret = fuel_gauge_reg_readb(info, AXP20X_FG_RES); 229 if (ret < 0) 230 goto out; 231 info->fg_res = ret; 232 233 ret = iio_read_channel_raw(info->iio_channel[BAT_VOLT], &info->bat_volt); 234 if (ret < 0) 235 goto out; 236 237 if (info->pwr_stat & PS_STAT_BAT_CHRG_DIR) { 238 info->d_curr = 0; 239 ret = iio_read_channel_raw(info->iio_channel[BAT_CHRG_CURR], &info->c_curr); 240 if (ret < 0) 241 goto out; 242 } else { 243 info->c_curr = 0; 244 ret = iio_read_channel_raw(info->iio_channel[BAT_D_CURR], &info->d_curr); 245 if (ret < 0) 246 goto out; 247 } 248 249 ret = fuel_gauge_read_12bit_word(info, AXP288_FG_OCVH_REG); 250 if (ret < 0) 251 goto out; 252 info->ocv = ret; 253 254 ret = fuel_gauge_read_15bit_word(info, AXP288_FG_CC_MTR1_REG); 255 if (ret < 0) 256 goto out; 257 info->fg_cc_mtr1 = ret; 258 259 ret = fuel_gauge_read_15bit_word(info, AXP288_FG_DES_CAP1_REG); 260 if (ret < 0) 261 goto out; 262 info->fg_des_cap1 = ret; 263 264 info->last_updated = jiffies; 265 info->valid = 1; 266 ret = 0; 267 out: 268 iosf_mbi_unblock_punit_i2c_access(); 269 return ret; 270 } 271 272 static void fuel_gauge_get_status(struct axp288_fg_info *info) 273 { 274 int pwr_stat = info->pwr_stat; 275 int fg_res = info->fg_res; 276 int curr = info->d_curr; 277 278 /* Report full if Vbus is valid and the reported capacity is 100% */ 279 if (!(pwr_stat & PS_STAT_VBUS_VALID)) 280 goto not_full; 281 282 if (!(fg_res & FG_REP_CAP_VALID)) 283 goto not_full; 284 285 fg_res &= ~FG_REP_CAP_VALID; 286 if (fg_res == 100) { 287 info->status = POWER_SUPPLY_STATUS_FULL; 288 return; 289 } 290 291 /* 292 * Sometimes the charger turns itself off before fg-res reaches 100%. 293 * When this happens the AXP288 reports a not-charging status and 294 * 0 mA discharge current. 295 */ 296 if (fg_res < 90 || (pwr_stat & PS_STAT_BAT_CHRG_DIR)) 297 goto not_full; 298 299 if (curr == 0) { 300 info->status = POWER_SUPPLY_STATUS_FULL; 301 return; 302 } 303 304 not_full: 305 if (pwr_stat & PS_STAT_BAT_CHRG_DIR) 306 info->status = POWER_SUPPLY_STATUS_CHARGING; 307 else 308 info->status = POWER_SUPPLY_STATUS_DISCHARGING; 309 } 310 311 static int fuel_gauge_battery_health(struct axp288_fg_info *info) 312 { 313 int vocv = VOLTAGE_FROM_ADC(info->ocv); 314 int health = POWER_SUPPLY_HEALTH_UNKNOWN; 315 316 if (vocv > info->max_volt) 317 health = POWER_SUPPLY_HEALTH_OVERVOLTAGE; 318 else 319 health = POWER_SUPPLY_HEALTH_GOOD; 320 321 return health; 322 } 323 324 static int fuel_gauge_get_property(struct power_supply *ps, 325 enum power_supply_property prop, 326 union power_supply_propval *val) 327 { 328 struct axp288_fg_info *info = power_supply_get_drvdata(ps); 329 int ret, value; 330 331 mutex_lock(&info->lock); 332 333 ret = fuel_gauge_update_registers(info); 334 if (ret < 0) 335 goto out; 336 337 switch (prop) { 338 case POWER_SUPPLY_PROP_STATUS: 339 fuel_gauge_get_status(info); 340 val->intval = info->status; 341 break; 342 case POWER_SUPPLY_PROP_HEALTH: 343 val->intval = fuel_gauge_battery_health(info); 344 break; 345 case POWER_SUPPLY_PROP_VOLTAGE_NOW: 346 value = VOLTAGE_FROM_ADC(info->bat_volt); 347 val->intval = PROP_VOLT(value); 348 break; 349 case POWER_SUPPLY_PROP_VOLTAGE_OCV: 350 value = VOLTAGE_FROM_ADC(info->ocv); 351 val->intval = PROP_VOLT(value); 352 break; 353 case POWER_SUPPLY_PROP_CURRENT_NOW: 354 if (info->d_curr > 0) 355 value = -1 * info->d_curr; 356 else 357 value = info->c_curr; 358 359 val->intval = PROP_CURR(value); 360 break; 361 case POWER_SUPPLY_PROP_PRESENT: 362 if (info->pwr_op & CHRG_STAT_BAT_PRESENT) 363 val->intval = 1; 364 else 365 val->intval = 0; 366 break; 367 case POWER_SUPPLY_PROP_CAPACITY: 368 if (!(info->fg_res & FG_REP_CAP_VALID)) 369 dev_err(info->dev, "capacity measurement not valid\n"); 370 val->intval = (info->fg_res & FG_REP_CAP_VAL_MASK); 371 break; 372 case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN: 373 val->intval = (info->low_cap & 0x0f); 374 break; 375 case POWER_SUPPLY_PROP_TECHNOLOGY: 376 val->intval = POWER_SUPPLY_TECHNOLOGY_LION; 377 break; 378 case POWER_SUPPLY_PROP_CHARGE_NOW: 379 val->intval = info->fg_cc_mtr1 * FG_DES_CAP_RES_LSB; 380 break; 381 case POWER_SUPPLY_PROP_CHARGE_FULL: 382 val->intval = info->fg_des_cap1 * FG_DES_CAP_RES_LSB; 383 break; 384 case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: 385 val->intval = PROP_VOLT(info->max_volt); 386 break; 387 default: 388 ret = -EINVAL; 389 } 390 391 out: 392 mutex_unlock(&info->lock); 393 return ret; 394 } 395 396 static int fuel_gauge_set_property(struct power_supply *ps, 397 enum power_supply_property prop, 398 const union power_supply_propval *val) 399 { 400 struct axp288_fg_info *info = power_supply_get_drvdata(ps); 401 int new_low_cap, ret = 0; 402 403 mutex_lock(&info->lock); 404 switch (prop) { 405 case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN: 406 if ((val->intval < 0) || (val->intval > 15)) { 407 ret = -EINVAL; 408 break; 409 } 410 new_low_cap = info->low_cap; 411 new_low_cap &= 0xf0; 412 new_low_cap |= (val->intval & 0xf); 413 ret = fuel_gauge_reg_writeb(info, AXP288_FG_LOW_CAP_REG, new_low_cap); 414 if (ret == 0) 415 info->low_cap = new_low_cap; 416 break; 417 default: 418 ret = -EINVAL; 419 break; 420 } 421 422 mutex_unlock(&info->lock); 423 return ret; 424 } 425 426 static int fuel_gauge_property_is_writeable(struct power_supply *psy, 427 enum power_supply_property psp) 428 { 429 int ret; 430 431 switch (psp) { 432 case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN: 433 ret = 1; 434 break; 435 default: 436 ret = 0; 437 } 438 439 return ret; 440 } 441 442 static irqreturn_t fuel_gauge_thread_handler(int irq, void *dev) 443 { 444 struct axp288_fg_info *info = dev; 445 int i; 446 447 for (i = 0; i < AXP288_FG_INTR_NUM; i++) { 448 if (info->irq[i] == irq) 449 break; 450 } 451 452 if (i >= AXP288_FG_INTR_NUM) { 453 dev_warn(info->dev, "spurious interrupt!!\n"); 454 return IRQ_NONE; 455 } 456 457 switch (i) { 458 case QWBTU_IRQ: 459 dev_info(info->dev, "Quit Battery under temperature in work mode IRQ (QWBTU)\n"); 460 break; 461 case WBTU_IRQ: 462 dev_info(info->dev, "Battery under temperature in work mode IRQ (WBTU)\n"); 463 break; 464 case QWBTO_IRQ: 465 dev_info(info->dev, "Quit Battery over temperature in work mode IRQ (QWBTO)\n"); 466 break; 467 case WBTO_IRQ: 468 dev_info(info->dev, "Battery over temperature in work mode IRQ (WBTO)\n"); 469 break; 470 case WL2_IRQ: 471 dev_info(info->dev, "Low Batt Warning(2) INTR\n"); 472 break; 473 case WL1_IRQ: 474 dev_info(info->dev, "Low Batt Warning(1) INTR\n"); 475 break; 476 default: 477 dev_warn(info->dev, "Spurious Interrupt!!!\n"); 478 } 479 480 info->valid = 0; /* Force updating of the cached registers */ 481 482 power_supply_changed(info->bat); 483 return IRQ_HANDLED; 484 } 485 486 static void fuel_gauge_external_power_changed(struct power_supply *psy) 487 { 488 struct axp288_fg_info *info = power_supply_get_drvdata(psy); 489 490 info->valid = 0; /* Force updating of the cached registers */ 491 power_supply_changed(info->bat); 492 } 493 494 static const struct power_supply_desc fuel_gauge_desc = { 495 .name = DEV_NAME, 496 .type = POWER_SUPPLY_TYPE_BATTERY, 497 .properties = fuel_gauge_props, 498 .num_properties = ARRAY_SIZE(fuel_gauge_props), 499 .get_property = fuel_gauge_get_property, 500 .set_property = fuel_gauge_set_property, 501 .property_is_writeable = fuel_gauge_property_is_writeable, 502 .external_power_changed = fuel_gauge_external_power_changed, 503 }; 504 505 static void fuel_gauge_init_irq(struct axp288_fg_info *info, struct platform_device *pdev) 506 { 507 int ret, i, pirq; 508 509 for (i = 0; i < AXP288_FG_INTR_NUM; i++) { 510 pirq = platform_get_irq(pdev, i); 511 info->irq[i] = regmap_irq_get_virq(info->regmap_irqc, pirq); 512 if (info->irq[i] < 0) { 513 dev_warn(info->dev, "regmap_irq get virq failed for IRQ %d: %d\n", 514 pirq, info->irq[i]); 515 info->irq[i] = -1; 516 goto intr_failed; 517 } 518 ret = request_threaded_irq(info->irq[i], 519 NULL, fuel_gauge_thread_handler, 520 IRQF_ONESHOT, DEV_NAME, info); 521 if (ret) { 522 dev_warn(info->dev, "request irq failed for IRQ %d: %d\n", 523 pirq, info->irq[i]); 524 info->irq[i] = -1; 525 goto intr_failed; 526 } 527 } 528 return; 529 530 intr_failed: 531 for (; i > 0; i--) { 532 free_irq(info->irq[i - 1], info); 533 info->irq[i - 1] = -1; 534 } 535 } 536 537 /* 538 * Some devices have no battery (HDMI sticks) and the axp288 battery's 539 * detection reports one despite it not being there. 540 * Please keep this listed sorted alphabetically. 541 */ 542 static const struct dmi_system_id axp288_no_battery_list[] = { 543 { 544 /* ACEPC T8 Cherry Trail Z8350 mini PC */ 545 .matches = { 546 DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "To be filled by O.E.M."), 547 DMI_EXACT_MATCH(DMI_BOARD_NAME, "Cherry Trail CR"), 548 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "T8"), 549 /* also match on somewhat unique bios-version */ 550 DMI_EXACT_MATCH(DMI_BIOS_VERSION, "1.000"), 551 }, 552 }, 553 { 554 /* ACEPC T11 Cherry Trail Z8350 mini PC */ 555 .matches = { 556 DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "To be filled by O.E.M."), 557 DMI_EXACT_MATCH(DMI_BOARD_NAME, "Cherry Trail CR"), 558 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "T11"), 559 /* also match on somewhat unique bios-version */ 560 DMI_EXACT_MATCH(DMI_BIOS_VERSION, "1.000"), 561 }, 562 }, 563 { 564 /* ECS EF20EA */ 565 .matches = { 566 DMI_MATCH(DMI_PRODUCT_NAME, "EF20EA"), 567 }, 568 }, 569 { 570 /* Intel Cherry Trail Compute Stick, Windows version */ 571 .matches = { 572 DMI_MATCH(DMI_SYS_VENDOR, "Intel"), 573 DMI_MATCH(DMI_PRODUCT_NAME, "STK1AW32SC"), 574 }, 575 }, 576 { 577 /* Intel Cherry Trail Compute Stick, version without an OS */ 578 .matches = { 579 DMI_MATCH(DMI_SYS_VENDOR, "Intel"), 580 DMI_MATCH(DMI_PRODUCT_NAME, "STK1A32SC"), 581 }, 582 }, 583 { 584 /* Meegopad T02 */ 585 .matches = { 586 DMI_MATCH(DMI_PRODUCT_NAME, "MEEGOPAD T02"), 587 }, 588 }, 589 { /* Mele PCG03 Mini PC */ 590 .matches = { 591 DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Mini PC"), 592 DMI_EXACT_MATCH(DMI_BOARD_NAME, "Mini PC"), 593 }, 594 }, 595 { 596 /* Minix Neo Z83-4 mini PC */ 597 .matches = { 598 DMI_MATCH(DMI_SYS_VENDOR, "MINIX"), 599 DMI_MATCH(DMI_PRODUCT_NAME, "Z83-4"), 600 } 601 }, 602 { 603 /* Various Ace PC/Meegopad/MinisForum/Wintel Mini-PCs/HDMI-sticks */ 604 .matches = { 605 DMI_MATCH(DMI_BOARD_NAME, "T3 MRD"), 606 DMI_MATCH(DMI_CHASSIS_TYPE, "3"), 607 DMI_MATCH(DMI_BIOS_VENDOR, "American Megatrends Inc."), 608 DMI_MATCH(DMI_BIOS_VERSION, "5.11"), 609 }, 610 }, 611 {} 612 }; 613 614 static int axp288_fuel_gauge_probe(struct platform_device *pdev) 615 { 616 int i, ret = 0; 617 struct axp288_fg_info *info; 618 struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent); 619 struct power_supply_config psy_cfg = {}; 620 static const char * const iio_chan_name[] = { 621 [BAT_CHRG_CURR] = "axp288-chrg-curr", 622 [BAT_D_CURR] = "axp288-chrg-d-curr", 623 [BAT_VOLT] = "axp288-batt-volt", 624 }; 625 unsigned int val; 626 627 if (dmi_check_system(axp288_no_battery_list)) 628 return -ENODEV; 629 630 info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); 631 if (!info) 632 return -ENOMEM; 633 634 info->dev = &pdev->dev; 635 info->regmap = axp20x->regmap; 636 info->regmap_irqc = axp20x->regmap_irqc; 637 info->status = POWER_SUPPLY_STATUS_UNKNOWN; 638 info->valid = 0; 639 640 platform_set_drvdata(pdev, info); 641 642 mutex_init(&info->lock); 643 644 for (i = 0; i < IIO_CHANNEL_NUM; i++) { 645 /* 646 * Note cannot use devm_iio_channel_get because x86 systems 647 * lack the device<->channel maps which iio_channel_get will 648 * try to use when passed a non NULL device pointer. 649 */ 650 info->iio_channel[i] = 651 iio_channel_get(NULL, iio_chan_name[i]); 652 if (IS_ERR(info->iio_channel[i])) { 653 ret = PTR_ERR(info->iio_channel[i]); 654 dev_dbg(&pdev->dev, "error getting iiochan %s: %d\n", 655 iio_chan_name[i], ret); 656 /* Wait for axp288_adc to load */ 657 if (ret == -ENODEV) 658 ret = -EPROBE_DEFER; 659 660 goto out_free_iio_chan; 661 } 662 } 663 664 ret = iosf_mbi_block_punit_i2c_access(); 665 if (ret < 0) 666 goto out_free_iio_chan; 667 668 /* 669 * On some devices the fuelgauge and charger parts of the axp288 are 670 * not used, check that the fuelgauge is enabled (CC_CTRL != 0). 671 */ 672 ret = regmap_read(axp20x->regmap, AXP20X_CC_CTRL, &val); 673 if (ret < 0) 674 goto unblock_punit_i2c_access; 675 if (val == 0) { 676 ret = -ENODEV; 677 goto unblock_punit_i2c_access; 678 } 679 680 ret = fuel_gauge_reg_readb(info, AXP288_FG_DES_CAP1_REG); 681 if (ret < 0) 682 goto unblock_punit_i2c_access; 683 684 if (!(ret & FG_DES_CAP1_VALID)) { 685 dev_err(&pdev->dev, "axp288 not configured by firmware\n"); 686 ret = -ENODEV; 687 goto unblock_punit_i2c_access; 688 } 689 690 ret = fuel_gauge_reg_readb(info, AXP20X_CHRG_CTRL1); 691 if (ret < 0) 692 goto unblock_punit_i2c_access; 693 switch ((ret & CHRG_CCCV_CV_MASK) >> CHRG_CCCV_CV_BIT_POS) { 694 case CHRG_CCCV_CV_4100MV: 695 info->max_volt = 4100; 696 break; 697 case CHRG_CCCV_CV_4150MV: 698 info->max_volt = 4150; 699 break; 700 case CHRG_CCCV_CV_4200MV: 701 info->max_volt = 4200; 702 break; 703 case CHRG_CCCV_CV_4350MV: 704 info->max_volt = 4350; 705 break; 706 } 707 708 ret = fuel_gauge_reg_readb(info, AXP20X_PWR_OP_MODE); 709 if (ret < 0) 710 goto unblock_punit_i2c_access; 711 info->pwr_op = ret; 712 713 ret = fuel_gauge_reg_readb(info, AXP288_FG_LOW_CAP_REG); 714 if (ret < 0) 715 goto unblock_punit_i2c_access; 716 info->low_cap = ret; 717 718 unblock_punit_i2c_access: 719 iosf_mbi_unblock_punit_i2c_access(); 720 /* In case we arrive here by goto because of a register access error */ 721 if (ret < 0) 722 goto out_free_iio_chan; 723 724 psy_cfg.drv_data = info; 725 info->bat = power_supply_register(&pdev->dev, &fuel_gauge_desc, &psy_cfg); 726 if (IS_ERR(info->bat)) { 727 ret = PTR_ERR(info->bat); 728 dev_err(&pdev->dev, "failed to register battery: %d\n", ret); 729 goto out_free_iio_chan; 730 } 731 732 fuel_gauge_init_irq(info, pdev); 733 734 return 0; 735 736 out_free_iio_chan: 737 for (i = 0; i < IIO_CHANNEL_NUM; i++) 738 if (!IS_ERR_OR_NULL(info->iio_channel[i])) 739 iio_channel_release(info->iio_channel[i]); 740 741 return ret; 742 } 743 744 static const struct platform_device_id axp288_fg_id_table[] = { 745 { .name = DEV_NAME }, 746 {}, 747 }; 748 MODULE_DEVICE_TABLE(platform, axp288_fg_id_table); 749 750 static int axp288_fuel_gauge_remove(struct platform_device *pdev) 751 { 752 struct axp288_fg_info *info = platform_get_drvdata(pdev); 753 int i; 754 755 power_supply_unregister(info->bat); 756 757 for (i = 0; i < AXP288_FG_INTR_NUM; i++) 758 if (info->irq[i] >= 0) 759 free_irq(info->irq[i], info); 760 761 for (i = 0; i < IIO_CHANNEL_NUM; i++) 762 iio_channel_release(info->iio_channel[i]); 763 764 return 0; 765 } 766 767 static struct platform_driver axp288_fuel_gauge_driver = { 768 .probe = axp288_fuel_gauge_probe, 769 .remove = axp288_fuel_gauge_remove, 770 .id_table = axp288_fg_id_table, 771 .driver = { 772 .name = DEV_NAME, 773 }, 774 }; 775 776 module_platform_driver(axp288_fuel_gauge_driver); 777 778 MODULE_AUTHOR("Ramakrishna Pallala <ramakrishna.pallala@intel.com>"); 779 MODULE_AUTHOR("Todd Brandt <todd.e.brandt@linux.intel.com>"); 780 MODULE_DESCRIPTION("Xpower AXP288 Fuel Gauge Driver"); 781 MODULE_LICENSE("GPL"); 782