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