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