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