88pm860x_charger.c (19152B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Battery driver for Marvell 88PM860x PMIC 4 * 5 * Copyright (c) 2012 Marvell International Ltd. 6 * Author: Jett Zhou <jtzhou@marvell.com> 7 * Haojian Zhuang <haojian.zhuang@marvell.com> 8 */ 9 10#include <linux/kernel.h> 11#include <linux/module.h> 12#include <linux/platform_device.h> 13#include <linux/slab.h> 14#include <linux/power_supply.h> 15#include <linux/mfd/88pm860x.h> 16#include <linux/delay.h> 17#include <linux/uaccess.h> 18#include <asm/div64.h> 19 20/* bit definitions of Status Query Interface 2 */ 21#define STATUS2_CHG (1 << 2) 22 23/* bit definitions of Reset Out Register */ 24#define RESET_SW_PD (1 << 7) 25 26/* bit definitions of PreReg 1 */ 27#define PREREG1_90MA (0x0) 28#define PREREG1_180MA (0x1) 29#define PREREG1_450MA (0x4) 30#define PREREG1_540MA (0x5) 31#define PREREG1_1350MA (0xE) 32#define PREREG1_VSYS_4_5V (3 << 4) 33 34/* bit definitions of Charger Control 1 Register */ 35#define CC1_MODE_OFF (0) 36#define CC1_MODE_PRECHARGE (1) 37#define CC1_MODE_FASTCHARGE (2) 38#define CC1_MODE_PULSECHARGE (3) 39#define CC1_ITERM_20MA (0 << 2) 40#define CC1_ITERM_60MA (2 << 2) 41#define CC1_VFCHG_4_2V (9 << 4) 42 43/* bit definitions of Charger Control 2 Register */ 44#define CC2_ICHG_100MA (0x1) 45#define CC2_ICHG_500MA (0x9) 46#define CC2_ICHG_1000MA (0x13) 47 48/* bit definitions of Charger Control 3 Register */ 49#define CC3_180MIN_TIMEOUT (0x6 << 4) 50#define CC3_270MIN_TIMEOUT (0x7 << 4) 51#define CC3_360MIN_TIMEOUT (0xA << 4) 52#define CC3_DISABLE_TIMEOUT (0xF << 4) 53 54/* bit definitions of Charger Control 4 Register */ 55#define CC4_IPRE_40MA (7) 56#define CC4_VPCHG_3_2V (3 << 4) 57#define CC4_IFCHG_MON_EN (1 << 6) 58#define CC4_BTEMP_MON_EN (1 << 7) 59 60/* bit definitions of Charger Control 6 Register */ 61#define CC6_BAT_OV_EN (1 << 2) 62#define CC6_BAT_UV_EN (1 << 3) 63#define CC6_UV_VBAT_SET (0x3 << 6) /* 2.8v */ 64 65/* bit definitions of Charger Control 7 Register */ 66#define CC7_BAT_REM_EN (1 << 3) 67#define CC7_IFSM_EN (1 << 7) 68 69/* bit definitions of Measurement Enable 1 Register */ 70#define MEAS1_VBAT (1 << 0) 71 72/* bit definitions of Measurement Enable 3 Register */ 73#define MEAS3_IBAT_EN (1 << 0) 74#define MEAS3_CC_EN (1 << 2) 75 76#define FSM_INIT 0 77#define FSM_DISCHARGE 1 78#define FSM_PRECHARGE 2 79#define FSM_FASTCHARGE 3 80 81#define PRECHARGE_THRESHOLD 3100 82#define POWEROFF_THRESHOLD 3400 83#define CHARGE_THRESHOLD 4000 84#define DISCHARGE_THRESHOLD 4180 85 86/* over-temperature on PM8606 setting */ 87#define OVER_TEMP_FLAG (1 << 6) 88#define OVTEMP_AUTORECOVER (1 << 3) 89 90/* over-voltage protect on vchg setting mv */ 91#define VCHG_NORMAL_LOW 4200 92#define VCHG_NORMAL_CHECK 5800 93#define VCHG_NORMAL_HIGH 6000 94#define VCHG_OVP_LOW 5500 95 96struct pm860x_charger_info { 97 struct pm860x_chip *chip; 98 struct i2c_client *i2c; 99 struct i2c_client *i2c_8606; 100 struct device *dev; 101 102 struct power_supply *usb; 103 struct mutex lock; 104 int irq_nums; 105 int irq[7]; 106 unsigned state:3; /* fsm state */ 107 unsigned online:1; /* usb charger */ 108 unsigned present:1; /* battery present */ 109 unsigned allowed:1; 110}; 111 112static char *pm860x_supplied_to[] = { 113 "battery-monitor", 114}; 115 116static int measure_vchg(struct pm860x_charger_info *info, int *data) 117{ 118 unsigned char buf[2]; 119 int ret = 0; 120 121 ret = pm860x_bulk_read(info->i2c, PM8607_VCHG_MEAS1, 2, buf); 122 if (ret < 0) 123 return ret; 124 125 *data = ((buf[0] & 0xff) << 4) | (buf[1] & 0x0f); 126 /* V_BATT_MEAS(mV) = value * 5 * 1.8 * 1000 / (2^12) */ 127 *data = ((*data & 0xfff) * 9 * 125) >> 9; 128 129 dev_dbg(info->dev, "%s, vchg: %d mv\n", __func__, *data); 130 131 return ret; 132} 133 134static void set_vchg_threshold(struct pm860x_charger_info *info, 135 int min, int max) 136{ 137 int data; 138 139 /* (tmp << 8) * / 5 / 1800 */ 140 if (min <= 0) 141 data = 0; 142 else 143 data = (min << 5) / 1125; 144 pm860x_reg_write(info->i2c, PM8607_VCHG_LOWTH, data); 145 dev_dbg(info->dev, "VCHG_LOWTH:%dmv, 0x%x\n", min, data); 146 147 if (max <= 0) 148 data = 0xff; 149 else 150 data = (max << 5) / 1125; 151 pm860x_reg_write(info->i2c, PM8607_VCHG_HIGHTH, data); 152 dev_dbg(info->dev, "VCHG_HIGHTH:%dmv, 0x%x\n", max, data); 153 154} 155 156static void set_vbatt_threshold(struct pm860x_charger_info *info, 157 int min, int max) 158{ 159 int data; 160 161 /* (tmp << 8) * 3 / 1800 */ 162 if (min <= 0) 163 data = 0; 164 else 165 data = (min << 5) / 675; 166 pm860x_reg_write(info->i2c, PM8607_VBAT_LOWTH, data); 167 dev_dbg(info->dev, "VBAT Min:%dmv, LOWTH:0x%x\n", min, data); 168 169 if (max <= 0) 170 data = 0xff; 171 else 172 data = (max << 5) / 675; 173 pm860x_reg_write(info->i2c, PM8607_VBAT_HIGHTH, data); 174 dev_dbg(info->dev, "VBAT Max:%dmv, HIGHTH:0x%x\n", max, data); 175 176 return; 177} 178 179static int start_precharge(struct pm860x_charger_info *info) 180{ 181 int ret; 182 183 dev_dbg(info->dev, "Start Pre-charging!\n"); 184 set_vbatt_threshold(info, 0, 0); 185 186 ret = pm860x_reg_write(info->i2c_8606, PM8606_PREREGULATORA, 187 PREREG1_1350MA | PREREG1_VSYS_4_5V); 188 if (ret < 0) 189 goto out; 190 /* stop charging */ 191 ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL1, 3, 192 CC1_MODE_OFF); 193 if (ret < 0) 194 goto out; 195 /* set 270 minutes timeout */ 196 ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL3, (0xf << 4), 197 CC3_270MIN_TIMEOUT); 198 if (ret < 0) 199 goto out; 200 /* set precharge current, termination voltage, IBAT & TBAT monitor */ 201 ret = pm860x_reg_write(info->i2c, PM8607_CHG_CTRL4, 202 CC4_IPRE_40MA | CC4_VPCHG_3_2V | 203 CC4_IFCHG_MON_EN | CC4_BTEMP_MON_EN); 204 if (ret < 0) 205 goto out; 206 ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL7, 207 CC7_BAT_REM_EN | CC7_IFSM_EN, 208 CC7_BAT_REM_EN | CC7_IFSM_EN); 209 if (ret < 0) 210 goto out; 211 /* trigger precharge */ 212 ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL1, 3, 213 CC1_MODE_PRECHARGE); 214out: 215 return ret; 216} 217 218static int start_fastcharge(struct pm860x_charger_info *info) 219{ 220 int ret; 221 222 dev_dbg(info->dev, "Start Fast-charging!\n"); 223 224 /* set fastcharge termination current & voltage, disable charging */ 225 ret = pm860x_reg_write(info->i2c, PM8607_CHG_CTRL1, 226 CC1_MODE_OFF | CC1_ITERM_60MA | 227 CC1_VFCHG_4_2V); 228 if (ret < 0) 229 goto out; 230 ret = pm860x_reg_write(info->i2c_8606, PM8606_PREREGULATORA, 231 PREREG1_540MA | PREREG1_VSYS_4_5V); 232 if (ret < 0) 233 goto out; 234 ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL2, 0x1f, 235 CC2_ICHG_500MA); 236 if (ret < 0) 237 goto out; 238 /* set 270 minutes timeout */ 239 ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL3, (0xf << 4), 240 CC3_270MIN_TIMEOUT); 241 if (ret < 0) 242 goto out; 243 /* set IBAT & TBAT monitor */ 244 ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL4, 245 CC4_IFCHG_MON_EN | CC4_BTEMP_MON_EN, 246 CC4_IFCHG_MON_EN | CC4_BTEMP_MON_EN); 247 if (ret < 0) 248 goto out; 249 ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL6, 250 CC6_BAT_OV_EN | CC6_BAT_UV_EN | 251 CC6_UV_VBAT_SET, 252 CC6_BAT_OV_EN | CC6_BAT_UV_EN | 253 CC6_UV_VBAT_SET); 254 if (ret < 0) 255 goto out; 256 ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL7, 257 CC7_BAT_REM_EN | CC7_IFSM_EN, 258 CC7_BAT_REM_EN | CC7_IFSM_EN); 259 if (ret < 0) 260 goto out; 261 /* launch fast-charge */ 262 ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL1, 3, 263 CC1_MODE_FASTCHARGE); 264 /* vchg threshold setting */ 265 set_vchg_threshold(info, VCHG_NORMAL_LOW, VCHG_NORMAL_HIGH); 266out: 267 return ret; 268} 269 270static void stop_charge(struct pm860x_charger_info *info, int vbatt) 271{ 272 dev_dbg(info->dev, "Stop charging!\n"); 273 pm860x_set_bits(info->i2c, PM8607_CHG_CTRL1, 3, CC1_MODE_OFF); 274 if (vbatt > CHARGE_THRESHOLD && info->online) 275 set_vbatt_threshold(info, CHARGE_THRESHOLD, 0); 276} 277 278static void power_off_notification(struct pm860x_charger_info *info) 279{ 280 dev_dbg(info->dev, "Power-off notification!\n"); 281} 282 283static int set_charging_fsm(struct pm860x_charger_info *info) 284{ 285 struct power_supply *psy; 286 union power_supply_propval data; 287 unsigned char fsm_state[][16] = { "init", "discharge", "precharge", 288 "fastcharge", 289 }; 290 int ret; 291 int vbatt; 292 293 psy = power_supply_get_by_name(pm860x_supplied_to[0]); 294 if (!psy) 295 return -EINVAL; 296 ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_VOLTAGE_NOW, 297 &data); 298 if (ret) { 299 power_supply_put(psy); 300 return ret; 301 } 302 vbatt = data.intval / 1000; 303 304 ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_PRESENT, &data); 305 if (ret) { 306 power_supply_put(psy); 307 return ret; 308 } 309 power_supply_put(psy); 310 311 mutex_lock(&info->lock); 312 info->present = data.intval; 313 314 dev_dbg(info->dev, "Entering FSM:%s, Charger:%s, Battery:%s, " 315 "Allowed:%d\n", 316 &fsm_state[info->state][0], 317 (info->online) ? "online" : "N/A", 318 (info->present) ? "present" : "N/A", info->allowed); 319 dev_dbg(info->dev, "set_charging_fsm:vbatt:%d(mV)\n", vbatt); 320 321 switch (info->state) { 322 case FSM_INIT: 323 if (info->online && info->present && info->allowed) { 324 if (vbatt < PRECHARGE_THRESHOLD) { 325 info->state = FSM_PRECHARGE; 326 start_precharge(info); 327 } else if (vbatt > DISCHARGE_THRESHOLD) { 328 info->state = FSM_DISCHARGE; 329 stop_charge(info, vbatt); 330 } else if (vbatt < DISCHARGE_THRESHOLD) { 331 info->state = FSM_FASTCHARGE; 332 start_fastcharge(info); 333 } 334 } else { 335 if (vbatt < POWEROFF_THRESHOLD) { 336 power_off_notification(info); 337 } else { 338 info->state = FSM_DISCHARGE; 339 stop_charge(info, vbatt); 340 } 341 } 342 break; 343 case FSM_PRECHARGE: 344 if (info->online && info->present && info->allowed) { 345 if (vbatt > PRECHARGE_THRESHOLD) { 346 info->state = FSM_FASTCHARGE; 347 start_fastcharge(info); 348 } 349 } else { 350 info->state = FSM_DISCHARGE; 351 stop_charge(info, vbatt); 352 } 353 break; 354 case FSM_FASTCHARGE: 355 if (info->online && info->present && info->allowed) { 356 if (vbatt < PRECHARGE_THRESHOLD) { 357 info->state = FSM_PRECHARGE; 358 start_precharge(info); 359 } 360 } else { 361 info->state = FSM_DISCHARGE; 362 stop_charge(info, vbatt); 363 } 364 break; 365 case FSM_DISCHARGE: 366 if (info->online && info->present && info->allowed) { 367 if (vbatt < PRECHARGE_THRESHOLD) { 368 info->state = FSM_PRECHARGE; 369 start_precharge(info); 370 } else if (vbatt < DISCHARGE_THRESHOLD) { 371 info->state = FSM_FASTCHARGE; 372 start_fastcharge(info); 373 } 374 } else { 375 if (vbatt < POWEROFF_THRESHOLD) 376 power_off_notification(info); 377 else if (vbatt > CHARGE_THRESHOLD && info->online) 378 set_vbatt_threshold(info, CHARGE_THRESHOLD, 0); 379 } 380 break; 381 default: 382 dev_warn(info->dev, "FSM meets wrong state:%d\n", 383 info->state); 384 break; 385 } 386 dev_dbg(info->dev, 387 "Out FSM:%s, Charger:%s, Battery:%s, Allowed:%d\n", 388 &fsm_state[info->state][0], 389 (info->online) ? "online" : "N/A", 390 (info->present) ? "present" : "N/A", info->allowed); 391 mutex_unlock(&info->lock); 392 393 return 0; 394} 395 396static irqreturn_t pm860x_charger_handler(int irq, void *data) 397{ 398 struct pm860x_charger_info *info = data; 399 int ret; 400 401 mutex_lock(&info->lock); 402 ret = pm860x_reg_read(info->i2c, PM8607_STATUS_2); 403 if (ret < 0) { 404 mutex_unlock(&info->lock); 405 goto out; 406 } 407 if (ret & STATUS2_CHG) { 408 info->online = 1; 409 info->allowed = 1; 410 } else { 411 info->online = 0; 412 info->allowed = 0; 413 } 414 mutex_unlock(&info->lock); 415 dev_dbg(info->dev, "%s, Charger:%s, Allowed:%d\n", __func__, 416 (info->online) ? "online" : "N/A", info->allowed); 417 418 set_charging_fsm(info); 419 420 power_supply_changed(info->usb); 421out: 422 return IRQ_HANDLED; 423} 424 425static irqreturn_t pm860x_temp_handler(int irq, void *data) 426{ 427 struct power_supply *psy; 428 struct pm860x_charger_info *info = data; 429 union power_supply_propval temp; 430 int value; 431 int ret; 432 433 psy = power_supply_get_by_name(pm860x_supplied_to[0]); 434 if (!psy) 435 return IRQ_HANDLED; 436 ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_TEMP, &temp); 437 if (ret) 438 goto out; 439 value = temp.intval / 10; 440 441 mutex_lock(&info->lock); 442 /* Temperature < -10 C or >40 C, Will not allow charge */ 443 if (value < -10 || value > 40) 444 info->allowed = 0; 445 else 446 info->allowed = 1; 447 dev_dbg(info->dev, "%s, Allowed: %d\n", __func__, info->allowed); 448 mutex_unlock(&info->lock); 449 450 set_charging_fsm(info); 451out: 452 power_supply_put(psy); 453 return IRQ_HANDLED; 454} 455 456static irqreturn_t pm860x_exception_handler(int irq, void *data) 457{ 458 struct pm860x_charger_info *info = data; 459 460 mutex_lock(&info->lock); 461 info->allowed = 0; 462 mutex_unlock(&info->lock); 463 dev_dbg(info->dev, "%s, irq: %d\n", __func__, irq); 464 465 set_charging_fsm(info); 466 return IRQ_HANDLED; 467} 468 469static irqreturn_t pm860x_done_handler(int irq, void *data) 470{ 471 struct pm860x_charger_info *info = data; 472 struct power_supply *psy; 473 union power_supply_propval val; 474 int ret; 475 int vbatt; 476 477 mutex_lock(&info->lock); 478 /* pre-charge done, will transimit to fast-charge stage */ 479 if (info->state == FSM_PRECHARGE) { 480 info->allowed = 1; 481 goto out; 482 } 483 /* 484 * Fast charge done, delay to read 485 * the correct status of CHG_DET. 486 */ 487 mdelay(5); 488 info->allowed = 0; 489 psy = power_supply_get_by_name(pm860x_supplied_to[0]); 490 if (!psy) 491 goto out; 492 ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_VOLTAGE_NOW, 493 &val); 494 if (ret) 495 goto out_psy_put; 496 vbatt = val.intval / 1000; 497 /* 498 * CHG_DONE interrupt is faster than CHG_DET interrupt when 499 * plug in/out usb, So we can not rely on info->online, we 500 * need check pm8607 status register to check usb is online 501 * or not, then we can decide it is real charge done 502 * automatically or it is triggered by usb plug out; 503 */ 504 ret = pm860x_reg_read(info->i2c, PM8607_STATUS_2); 505 if (ret < 0) 506 goto out_psy_put; 507 if (vbatt > CHARGE_THRESHOLD && ret & STATUS2_CHG) 508 power_supply_set_property(psy, POWER_SUPPLY_PROP_CHARGE_FULL, 509 &val); 510 511out_psy_put: 512 power_supply_put(psy); 513out: 514 mutex_unlock(&info->lock); 515 dev_dbg(info->dev, "%s, Allowed: %d\n", __func__, info->allowed); 516 set_charging_fsm(info); 517 518 return IRQ_HANDLED; 519} 520 521static irqreturn_t pm860x_vbattery_handler(int irq, void *data) 522{ 523 struct pm860x_charger_info *info = data; 524 525 mutex_lock(&info->lock); 526 527 set_vbatt_threshold(info, 0, 0); 528 529 if (info->present && info->online) 530 info->allowed = 1; 531 else 532 info->allowed = 0; 533 mutex_unlock(&info->lock); 534 dev_dbg(info->dev, "%s, Allowed: %d\n", __func__, info->allowed); 535 536 set_charging_fsm(info); 537 538 return IRQ_HANDLED; 539} 540 541static irqreturn_t pm860x_vchg_handler(int irq, void *data) 542{ 543 struct pm860x_charger_info *info = data; 544 int vchg = 0; 545 546 if (info->present) 547 goto out; 548 549 measure_vchg(info, &vchg); 550 551 mutex_lock(&info->lock); 552 if (!info->online) { 553 int status; 554 /* check if over-temp on pm8606 or not */ 555 status = pm860x_reg_read(info->i2c_8606, PM8606_FLAGS); 556 if (status & OVER_TEMP_FLAG) { 557 /* clear over temp flag and set auto recover */ 558 pm860x_set_bits(info->i2c_8606, PM8606_FLAGS, 559 OVER_TEMP_FLAG, OVER_TEMP_FLAG); 560 pm860x_set_bits(info->i2c_8606, 561 PM8606_VSYS, 562 OVTEMP_AUTORECOVER, 563 OVTEMP_AUTORECOVER); 564 dev_dbg(info->dev, 565 "%s, pm8606 over-temp occurred\n", __func__); 566 } 567 } 568 569 if (vchg > VCHG_NORMAL_CHECK) { 570 set_vchg_threshold(info, VCHG_OVP_LOW, 0); 571 info->allowed = 0; 572 dev_dbg(info->dev, 573 "%s,pm8607 over-vchg occurred,vchg = %dmv\n", 574 __func__, vchg); 575 } else if (vchg < VCHG_OVP_LOW) { 576 set_vchg_threshold(info, VCHG_NORMAL_LOW, 577 VCHG_NORMAL_HIGH); 578 info->allowed = 1; 579 dev_dbg(info->dev, 580 "%s,pm8607 over-vchg recover,vchg = %dmv\n", 581 __func__, vchg); 582 } 583 mutex_unlock(&info->lock); 584 585 dev_dbg(info->dev, "%s, Allowed: %d\n", __func__, info->allowed); 586 set_charging_fsm(info); 587out: 588 return IRQ_HANDLED; 589} 590 591static int pm860x_usb_get_prop(struct power_supply *psy, 592 enum power_supply_property psp, 593 union power_supply_propval *val) 594{ 595 struct pm860x_charger_info *info = power_supply_get_drvdata(psy); 596 597 switch (psp) { 598 case POWER_SUPPLY_PROP_STATUS: 599 if (info->state == FSM_FASTCHARGE || 600 info->state == FSM_PRECHARGE) 601 val->intval = POWER_SUPPLY_STATUS_CHARGING; 602 else 603 val->intval = POWER_SUPPLY_STATUS_DISCHARGING; 604 break; 605 case POWER_SUPPLY_PROP_ONLINE: 606 val->intval = info->online; 607 break; 608 default: 609 return -ENODEV; 610 } 611 return 0; 612} 613 614static enum power_supply_property pm860x_usb_props[] = { 615 POWER_SUPPLY_PROP_STATUS, 616 POWER_SUPPLY_PROP_ONLINE, 617}; 618 619static int pm860x_init_charger(struct pm860x_charger_info *info) 620{ 621 int ret; 622 623 ret = pm860x_reg_read(info->i2c, PM8607_STATUS_2); 624 if (ret < 0) 625 return ret; 626 627 mutex_lock(&info->lock); 628 info->state = FSM_INIT; 629 if (ret & STATUS2_CHG) { 630 info->online = 1; 631 info->allowed = 1; 632 } else { 633 info->online = 0; 634 info->allowed = 0; 635 } 636 mutex_unlock(&info->lock); 637 638 set_charging_fsm(info); 639 return 0; 640} 641 642static struct pm860x_irq_desc { 643 const char *name; 644 irqreturn_t (*handler)(int irq, void *data); 645} pm860x_irq_descs[] = { 646 { "usb supply detect", pm860x_charger_handler }, 647 { "charge done", pm860x_done_handler }, 648 { "charge timeout", pm860x_exception_handler }, 649 { "charge fault", pm860x_exception_handler }, 650 { "temperature", pm860x_temp_handler }, 651 { "vbatt", pm860x_vbattery_handler }, 652 { "vchg", pm860x_vchg_handler }, 653}; 654 655static const struct power_supply_desc pm860x_charger_desc = { 656 .name = "usb", 657 .type = POWER_SUPPLY_TYPE_USB, 658 .properties = pm860x_usb_props, 659 .num_properties = ARRAY_SIZE(pm860x_usb_props), 660 .get_property = pm860x_usb_get_prop, 661}; 662 663static int pm860x_charger_probe(struct platform_device *pdev) 664{ 665 struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent); 666 struct power_supply_config psy_cfg = {}; 667 struct pm860x_charger_info *info; 668 int ret; 669 int count; 670 int i; 671 int j; 672 673 info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); 674 if (!info) 675 return -ENOMEM; 676 677 count = pdev->num_resources; 678 for (i = 0, j = 0; i < count; i++) { 679 info->irq[j] = platform_get_irq(pdev, i); 680 if (info->irq[j] < 0) 681 continue; 682 j++; 683 } 684 info->irq_nums = j; 685 686 info->chip = chip; 687 info->i2c = 688 (chip->id == CHIP_PM8607) ? chip->client : chip->companion; 689 info->i2c_8606 = 690 (chip->id == CHIP_PM8607) ? chip->companion : chip->client; 691 if (!info->i2c_8606) { 692 dev_err(&pdev->dev, "Missed I2C address of 88PM8606!\n"); 693 ret = -EINVAL; 694 goto out; 695 } 696 info->dev = &pdev->dev; 697 698 /* set init value for the case we are not using battery */ 699 set_vchg_threshold(info, VCHG_NORMAL_LOW, VCHG_OVP_LOW); 700 701 mutex_init(&info->lock); 702 platform_set_drvdata(pdev, info); 703 704 psy_cfg.drv_data = info; 705 psy_cfg.supplied_to = pm860x_supplied_to; 706 psy_cfg.num_supplicants = ARRAY_SIZE(pm860x_supplied_to); 707 info->usb = power_supply_register(&pdev->dev, &pm860x_charger_desc, 708 &psy_cfg); 709 if (IS_ERR(info->usb)) { 710 ret = PTR_ERR(info->usb); 711 goto out; 712 } 713 714 pm860x_init_charger(info); 715 716 for (i = 0; i < ARRAY_SIZE(info->irq); i++) { 717 ret = request_threaded_irq(info->irq[i], NULL, 718 pm860x_irq_descs[i].handler, 719 IRQF_ONESHOT, pm860x_irq_descs[i].name, info); 720 if (ret < 0) { 721 dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n", 722 info->irq[i], ret); 723 goto out_irq; 724 } 725 } 726 return 0; 727 728out_irq: 729 power_supply_unregister(info->usb); 730 while (--i >= 0) 731 free_irq(info->irq[i], info); 732out: 733 return ret; 734} 735 736static int pm860x_charger_remove(struct platform_device *pdev) 737{ 738 struct pm860x_charger_info *info = platform_get_drvdata(pdev); 739 int i; 740 741 power_supply_unregister(info->usb); 742 for (i = 0; i < info->irq_nums; i++) 743 free_irq(info->irq[i], info); 744 return 0; 745} 746 747static struct platform_driver pm860x_charger_driver = { 748 .driver = { 749 .name = "88pm860x-charger", 750 }, 751 .probe = pm860x_charger_probe, 752 .remove = pm860x_charger_remove, 753}; 754module_platform_driver(pm860x_charger_driver); 755 756MODULE_DESCRIPTION("Marvell 88PM860x Charger driver"); 757MODULE_LICENSE("GPL");