.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * Motorola CPCAP PMIC battery charger driver |
---|
3 | 4 | * |
---|
.. | .. |
---|
7 | 8 | * on earlier driver found in the Motorola Linux kernel: |
---|
8 | 9 | * |
---|
9 | 10 | * Copyright (C) 2009-2010 Motorola, Inc. |
---|
10 | | - * |
---|
11 | | - * This program is free software; you can redistribute it and/or modify |
---|
12 | | - * it under the terms of the GNU General Public License version 2 as |
---|
13 | | - * published by the Free Software Foundation. |
---|
14 | | - * |
---|
15 | | - * This program is distributed in the hope that it will be useful, |
---|
16 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
17 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
18 | | - * GNU General Public License for more details. |
---|
19 | 11 | */ |
---|
20 | 12 | |
---|
21 | 13 | #include <linux/atomic.h> |
---|
.. | .. |
---|
116 | 108 | #define CPCAP_REG_CRM_ICHRG_1A596 CPCAP_REG_CRM_ICHRG(0xe) |
---|
117 | 109 | #define CPCAP_REG_CRM_ICHRG_NO_LIMIT CPCAP_REG_CRM_ICHRG(0xf) |
---|
118 | 110 | |
---|
| 111 | +/* CPCAP_REG_VUSBC register bits needed for VBUS */ |
---|
| 112 | +#define CPCAP_BIT_VBUS_SWITCH BIT(0) /* VBUS boost to 5V */ |
---|
| 113 | + |
---|
119 | 114 | enum { |
---|
120 | 115 | CPCAP_CHARGER_IIO_BATTDET, |
---|
121 | 116 | CPCAP_CHARGER_IIO_VOLTAGE, |
---|
.. | .. |
---|
123 | 118 | CPCAP_CHARGER_IIO_CHRG_CURRENT, |
---|
124 | 119 | CPCAP_CHARGER_IIO_BATT_CURRENT, |
---|
125 | 120 | CPCAP_CHARGER_IIO_NR, |
---|
| 121 | +}; |
---|
| 122 | + |
---|
| 123 | +enum { |
---|
| 124 | + CPCAP_CHARGER_DISCONNECTED, |
---|
| 125 | + CPCAP_CHARGER_DETECTING, |
---|
| 126 | + CPCAP_CHARGER_CHARGING, |
---|
| 127 | + CPCAP_CHARGER_DONE, |
---|
126 | 128 | }; |
---|
127 | 129 | |
---|
128 | 130 | struct cpcap_charger_ddata { |
---|
.. | .. |
---|
138 | 140 | struct power_supply *usb; |
---|
139 | 141 | |
---|
140 | 142 | struct phy_companion comparator; /* For USB VBUS */ |
---|
141 | | - bool vbus_enabled; |
---|
| 143 | + unsigned int vbus_enabled:1; |
---|
| 144 | + unsigned int feeding_vbus:1; |
---|
142 | 145 | atomic_t active; |
---|
143 | 146 | |
---|
144 | 147 | int status; |
---|
| 148 | + int state; |
---|
| 149 | + int voltage; |
---|
145 | 150 | }; |
---|
146 | 151 | |
---|
147 | 152 | struct cpcap_interrupt_desc { |
---|
.. | .. |
---|
157 | 162 | |
---|
158 | 163 | bool chrg_se1b; |
---|
159 | 164 | bool rvrs_mode; |
---|
| 165 | + bool chrgcurr2; |
---|
160 | 166 | bool chrgcurr1; |
---|
161 | 167 | bool vbusvld; |
---|
162 | 168 | |
---|
.. | .. |
---|
166 | 172 | static enum power_supply_property cpcap_charger_props[] = { |
---|
167 | 173 | POWER_SUPPLY_PROP_STATUS, |
---|
168 | 174 | POWER_SUPPLY_PROP_ONLINE, |
---|
| 175 | + POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, |
---|
169 | 176 | POWER_SUPPLY_PROP_VOLTAGE_NOW, |
---|
170 | 177 | POWER_SUPPLY_PROP_CURRENT_NOW, |
---|
171 | 178 | }; |
---|
172 | 179 | |
---|
| 180 | +/* No battery always shows temperature of -40000 */ |
---|
173 | 181 | static bool cpcap_charger_battery_found(struct cpcap_charger_ddata *ddata) |
---|
174 | 182 | { |
---|
175 | 183 | struct iio_channel *channel; |
---|
176 | | - int error, value; |
---|
| 184 | + int error, temperature; |
---|
177 | 185 | |
---|
178 | 186 | channel = ddata->channels[CPCAP_CHARGER_IIO_BATTDET]; |
---|
179 | | - error = iio_read_channel_raw(channel, &value); |
---|
| 187 | + error = iio_read_channel_processed(channel, &temperature); |
---|
180 | 188 | if (error < 0) { |
---|
181 | 189 | dev_warn(ddata->dev, "%s failed: %i\n", __func__, error); |
---|
182 | 190 | |
---|
183 | 191 | return false; |
---|
184 | 192 | } |
---|
185 | 193 | |
---|
186 | | - return value == 1; |
---|
| 194 | + return temperature > -20000 && temperature < 60000; |
---|
187 | 195 | } |
---|
188 | 196 | |
---|
189 | 197 | static int cpcap_charger_get_charge_voltage(struct cpcap_charger_ddata *ddata) |
---|
.. | .. |
---|
228 | 236 | case POWER_SUPPLY_PROP_STATUS: |
---|
229 | 237 | val->intval = ddata->status; |
---|
230 | 238 | break; |
---|
| 239 | + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: |
---|
| 240 | + val->intval = ddata->voltage; |
---|
| 241 | + break; |
---|
231 | 242 | case POWER_SUPPLY_PROP_VOLTAGE_NOW: |
---|
232 | 243 | if (ddata->status == POWER_SUPPLY_STATUS_CHARGING) |
---|
233 | 244 | val->intval = cpcap_charger_get_charge_voltage(ddata) * |
---|
.. | .. |
---|
250 | 261 | } |
---|
251 | 262 | |
---|
252 | 263 | return 0; |
---|
| 264 | +} |
---|
| 265 | + |
---|
| 266 | +static int cpcap_charger_match_voltage(int voltage) |
---|
| 267 | +{ |
---|
| 268 | + switch (voltage) { |
---|
| 269 | + case 0 ... 4100000 - 1: return 3800000; |
---|
| 270 | + case 4100000 ... 4120000 - 1: return 4100000; |
---|
| 271 | + case 4120000 ... 4150000 - 1: return 4120000; |
---|
| 272 | + case 4150000 ... 4170000 - 1: return 4150000; |
---|
| 273 | + case 4170000 ... 4200000 - 1: return 4170000; |
---|
| 274 | + case 4200000 ... 4230000 - 1: return 4200000; |
---|
| 275 | + case 4230000 ... 4250000 - 1: return 4230000; |
---|
| 276 | + case 4250000 ... 4270000 - 1: return 4250000; |
---|
| 277 | + case 4270000 ... 4300000 - 1: return 4270000; |
---|
| 278 | + case 4300000 ... 4330000 - 1: return 4300000; |
---|
| 279 | + case 4330000 ... 4350000 - 1: return 4330000; |
---|
| 280 | + case 4350000 ... 4380000 - 1: return 4350000; |
---|
| 281 | + case 4380000 ... 4400000 - 1: return 4380000; |
---|
| 282 | + case 4400000 ... 4420000 - 1: return 4400000; |
---|
| 283 | + case 4420000 ... 4440000 - 1: return 4420000; |
---|
| 284 | + case 4440000: return 4440000; |
---|
| 285 | + default: return 0; |
---|
| 286 | + } |
---|
| 287 | +} |
---|
| 288 | + |
---|
| 289 | +static int |
---|
| 290 | +cpcap_charger_get_bat_const_charge_voltage(struct cpcap_charger_ddata *ddata) |
---|
| 291 | +{ |
---|
| 292 | + union power_supply_propval prop; |
---|
| 293 | + struct power_supply *battery; |
---|
| 294 | + int voltage = ddata->voltage; |
---|
| 295 | + int error; |
---|
| 296 | + |
---|
| 297 | + battery = power_supply_get_by_name("battery"); |
---|
| 298 | + if (battery) { |
---|
| 299 | + error = power_supply_get_property(battery, |
---|
| 300 | + POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, |
---|
| 301 | + &prop); |
---|
| 302 | + if (!error) |
---|
| 303 | + voltage = prop.intval; |
---|
| 304 | + |
---|
| 305 | + power_supply_put(battery); |
---|
| 306 | + } |
---|
| 307 | + |
---|
| 308 | + return voltage; |
---|
| 309 | +} |
---|
| 310 | + |
---|
| 311 | +static int cpcap_charger_set_property(struct power_supply *psy, |
---|
| 312 | + enum power_supply_property psp, |
---|
| 313 | + const union power_supply_propval *val) |
---|
| 314 | +{ |
---|
| 315 | + struct cpcap_charger_ddata *ddata = dev_get_drvdata(psy->dev.parent); |
---|
| 316 | + int voltage, batvolt; |
---|
| 317 | + |
---|
| 318 | + switch (psp) { |
---|
| 319 | + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: |
---|
| 320 | + voltage = cpcap_charger_match_voltage(val->intval); |
---|
| 321 | + batvolt = cpcap_charger_get_bat_const_charge_voltage(ddata); |
---|
| 322 | + if (voltage > batvolt) |
---|
| 323 | + voltage = batvolt; |
---|
| 324 | + ddata->voltage = voltage; |
---|
| 325 | + schedule_delayed_work(&ddata->detect_work, 0); |
---|
| 326 | + break; |
---|
| 327 | + default: |
---|
| 328 | + return -EINVAL; |
---|
| 329 | + } |
---|
| 330 | + |
---|
| 331 | + return 0; |
---|
| 332 | +} |
---|
| 333 | + |
---|
| 334 | +static int cpcap_charger_property_is_writeable(struct power_supply *psy, |
---|
| 335 | + enum power_supply_property psp) |
---|
| 336 | +{ |
---|
| 337 | + switch (psp) { |
---|
| 338 | + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: |
---|
| 339 | + return 1; |
---|
| 340 | + default: |
---|
| 341 | + return 0; |
---|
| 342 | + } |
---|
253 | 343 | } |
---|
254 | 344 | |
---|
255 | 345 | static void cpcap_charger_set_cable_path(struct cpcap_charger_ddata *ddata, |
---|
.. | .. |
---|
333 | 423 | } |
---|
334 | 424 | |
---|
335 | 425 | /* VBUS control functions for the USB PHY companion */ |
---|
336 | | - |
---|
337 | 426 | static void cpcap_charger_vbus_work(struct work_struct *work) |
---|
338 | 427 | { |
---|
339 | 428 | struct cpcap_charger_ddata *ddata; |
---|
.. | .. |
---|
351 | 440 | return; |
---|
352 | 441 | } |
---|
353 | 442 | |
---|
| 443 | + ddata->feeding_vbus = true; |
---|
354 | 444 | cpcap_charger_set_cable_path(ddata, false); |
---|
355 | 445 | cpcap_charger_set_inductive_path(ddata, false); |
---|
356 | 446 | |
---|
357 | 447 | error = cpcap_charger_set_state(ddata, 0, 0, 0); |
---|
| 448 | + if (error) |
---|
| 449 | + goto out_err; |
---|
| 450 | + |
---|
| 451 | + error = regmap_update_bits(ddata->reg, CPCAP_REG_VUSBC, |
---|
| 452 | + CPCAP_BIT_VBUS_SWITCH, |
---|
| 453 | + CPCAP_BIT_VBUS_SWITCH); |
---|
358 | 454 | if (error) |
---|
359 | 455 | goto out_err; |
---|
360 | 456 | |
---|
.. | .. |
---|
364 | 460 | if (error) |
---|
365 | 461 | goto out_err; |
---|
366 | 462 | } else { |
---|
| 463 | + error = regmap_update_bits(ddata->reg, CPCAP_REG_VUSBC, |
---|
| 464 | + CPCAP_BIT_VBUS_SWITCH, 0); |
---|
| 465 | + if (error) |
---|
| 466 | + goto out_err; |
---|
| 467 | + |
---|
367 | 468 | error = regmap_update_bits(ddata->reg, CPCAP_REG_CRM, |
---|
368 | 469 | CPCAP_REG_CRM_RVRSMODE, 0); |
---|
369 | 470 | if (error) |
---|
.. | .. |
---|
371 | 472 | |
---|
372 | 473 | cpcap_charger_set_cable_path(ddata, true); |
---|
373 | 474 | cpcap_charger_set_inductive_path(ddata, true); |
---|
| 475 | + ddata->feeding_vbus = false; |
---|
374 | 476 | } |
---|
375 | 477 | |
---|
376 | 478 | return; |
---|
.. | .. |
---|
414 | 516 | |
---|
415 | 517 | s->chrg_se1b = val & BIT(13); |
---|
416 | 518 | s->rvrs_mode = val & BIT(6); |
---|
| 519 | + s->chrgcurr2 = val & BIT(5); |
---|
417 | 520 | s->chrgcurr1 = val & BIT(4); |
---|
418 | 521 | s->vbusvld = val & BIT(3); |
---|
419 | 522 | |
---|
.. | .. |
---|
424 | 527 | s->battdetb = val & BIT(6); |
---|
425 | 528 | |
---|
426 | 529 | return 0; |
---|
| 530 | +} |
---|
| 531 | + |
---|
| 532 | +static void cpcap_charger_update_state(struct cpcap_charger_ddata *ddata, |
---|
| 533 | + int state) |
---|
| 534 | +{ |
---|
| 535 | + const char *status; |
---|
| 536 | + |
---|
| 537 | + if (state > CPCAP_CHARGER_DONE) { |
---|
| 538 | + dev_warn(ddata->dev, "unknown state: %i\n", state); |
---|
| 539 | + |
---|
| 540 | + return; |
---|
| 541 | + } |
---|
| 542 | + |
---|
| 543 | + ddata->state = state; |
---|
| 544 | + |
---|
| 545 | + switch (state) { |
---|
| 546 | + case CPCAP_CHARGER_DISCONNECTED: |
---|
| 547 | + status = "DISCONNECTED"; |
---|
| 548 | + break; |
---|
| 549 | + case CPCAP_CHARGER_DETECTING: |
---|
| 550 | + status = "DETECTING"; |
---|
| 551 | + break; |
---|
| 552 | + case CPCAP_CHARGER_CHARGING: |
---|
| 553 | + status = "CHARGING"; |
---|
| 554 | + break; |
---|
| 555 | + case CPCAP_CHARGER_DONE: |
---|
| 556 | + status = "DONE"; |
---|
| 557 | + break; |
---|
| 558 | + default: |
---|
| 559 | + return; |
---|
| 560 | + } |
---|
| 561 | + |
---|
| 562 | + dev_dbg(ddata->dev, "state: %s\n", status); |
---|
| 563 | +} |
---|
| 564 | + |
---|
| 565 | +static int cpcap_charger_voltage_to_regval(int voltage) |
---|
| 566 | +{ |
---|
| 567 | + int offset; |
---|
| 568 | + |
---|
| 569 | + switch (voltage) { |
---|
| 570 | + case 0 ... 4100000 - 1: |
---|
| 571 | + return 0; |
---|
| 572 | + case 4100000 ... 4200000 - 1: |
---|
| 573 | + offset = 1; |
---|
| 574 | + break; |
---|
| 575 | + case 4200000 ... 4300000 - 1: |
---|
| 576 | + offset = 0; |
---|
| 577 | + break; |
---|
| 578 | + case 4300000 ... 4380000 - 1: |
---|
| 579 | + offset = -1; |
---|
| 580 | + break; |
---|
| 581 | + case 4380000 ... 4440000: |
---|
| 582 | + offset = -2; |
---|
| 583 | + break; |
---|
| 584 | + default: |
---|
| 585 | + return 0; |
---|
| 586 | + } |
---|
| 587 | + |
---|
| 588 | + return ((voltage - 4100000) / 20000) + offset; |
---|
| 589 | +} |
---|
| 590 | + |
---|
| 591 | +static void cpcap_charger_disconnect(struct cpcap_charger_ddata *ddata, |
---|
| 592 | + int state, unsigned long delay) |
---|
| 593 | +{ |
---|
| 594 | + int error; |
---|
| 595 | + |
---|
| 596 | + error = cpcap_charger_set_state(ddata, 0, 0, 0); |
---|
| 597 | + if (error) |
---|
| 598 | + return; |
---|
| 599 | + |
---|
| 600 | + cpcap_charger_update_state(ddata, state); |
---|
| 601 | + power_supply_changed(ddata->usb); |
---|
| 602 | + schedule_delayed_work(&ddata->detect_work, delay); |
---|
427 | 603 | } |
---|
428 | 604 | |
---|
429 | 605 | static void cpcap_usb_detect(struct work_struct *work) |
---|
.. | .. |
---|
439 | 615 | if (error) |
---|
440 | 616 | return; |
---|
441 | 617 | |
---|
442 | | - if (cpcap_charger_vbus_valid(ddata) && s.chrgcurr1) { |
---|
| 618 | + /* Just init the state if a charger is connected with no chrg_det set */ |
---|
| 619 | + if (!s.chrg_det && s.chrgcurr1 && s.vbusvld) { |
---|
| 620 | + cpcap_charger_update_state(ddata, CPCAP_CHARGER_DETECTING); |
---|
| 621 | + |
---|
| 622 | + return; |
---|
| 623 | + } |
---|
| 624 | + |
---|
| 625 | + /* |
---|
| 626 | + * If battery voltage is higher than charge voltage, it may have been |
---|
| 627 | + * charged to 4.35V by Android. Try again in 10 minutes. |
---|
| 628 | + */ |
---|
| 629 | + if (cpcap_charger_get_charge_voltage(ddata) > ddata->voltage) { |
---|
| 630 | + cpcap_charger_disconnect(ddata, CPCAP_CHARGER_DETECTING, |
---|
| 631 | + HZ * 60 * 10); |
---|
| 632 | + |
---|
| 633 | + return; |
---|
| 634 | + } |
---|
| 635 | + |
---|
| 636 | + /* Delay for 80ms to avoid vbus bouncing when usb cable is plugged in */ |
---|
| 637 | + usleep_range(80000, 120000); |
---|
| 638 | + |
---|
| 639 | + /* Throttle chrgcurr2 interrupt for charger done and retry */ |
---|
| 640 | + switch (ddata->state) { |
---|
| 641 | + case CPCAP_CHARGER_CHARGING: |
---|
| 642 | + if (s.chrgcurr2) |
---|
| 643 | + break; |
---|
| 644 | + if (s.chrgcurr1 && s.vbusvld) { |
---|
| 645 | + cpcap_charger_disconnect(ddata, CPCAP_CHARGER_DONE, |
---|
| 646 | + HZ * 5); |
---|
| 647 | + return; |
---|
| 648 | + } |
---|
| 649 | + break; |
---|
| 650 | + case CPCAP_CHARGER_DONE: |
---|
| 651 | + if (!s.chrgcurr2) |
---|
| 652 | + break; |
---|
| 653 | + cpcap_charger_disconnect(ddata, CPCAP_CHARGER_DETECTING, |
---|
| 654 | + HZ * 5); |
---|
| 655 | + return; |
---|
| 656 | + default: |
---|
| 657 | + break; |
---|
| 658 | + } |
---|
| 659 | + |
---|
| 660 | + if (!ddata->feeding_vbus && cpcap_charger_vbus_valid(ddata) && |
---|
| 661 | + s.chrgcurr1) { |
---|
443 | 662 | int max_current; |
---|
| 663 | + int vchrg; |
---|
444 | 664 | |
---|
445 | 665 | if (cpcap_charger_battery_found(ddata)) |
---|
446 | 666 | max_current = CPCAP_REG_CRM_ICHRG_1A596; |
---|
447 | 667 | else |
---|
448 | 668 | max_current = CPCAP_REG_CRM_ICHRG_0A532; |
---|
449 | 669 | |
---|
| 670 | + vchrg = cpcap_charger_voltage_to_regval(ddata->voltage); |
---|
450 | 671 | error = cpcap_charger_set_state(ddata, |
---|
451 | | - CPCAP_REG_CRM_VCHRG_4V35, |
---|
| 672 | + CPCAP_REG_CRM_VCHRG(vchrg), |
---|
452 | 673 | max_current, 0); |
---|
453 | 674 | if (error) |
---|
454 | 675 | goto out_err; |
---|
| 676 | + cpcap_charger_update_state(ddata, CPCAP_CHARGER_CHARGING); |
---|
455 | 677 | } else { |
---|
456 | 678 | error = cpcap_charger_set_state(ddata, 0, 0, 0); |
---|
457 | 679 | if (error) |
---|
458 | 680 | goto out_err; |
---|
| 681 | + cpcap_charger_update_state(ddata, CPCAP_CHARGER_DISCONNECTED); |
---|
459 | 682 | } |
---|
460 | 683 | |
---|
461 | 684 | power_supply_changed(ddata->usb); |
---|
.. | .. |
---|
490 | 713 | |
---|
491 | 714 | error = devm_request_threaded_irq(ddata->dev, irq, NULL, |
---|
492 | 715 | cpcap_charger_irq_thread, |
---|
493 | | - IRQF_SHARED, |
---|
| 716 | + IRQF_SHARED | IRQF_ONESHOT, |
---|
494 | 717 | name, ddata); |
---|
495 | 718 | if (error) { |
---|
496 | 719 | dev_err(ddata->dev, "could not get irq %s: %i\n", |
---|
.. | .. |
---|
515 | 738 | "chrg_det", "rvrs_chrg", |
---|
516 | 739 | |
---|
517 | 740 | /* REG_INT1 */ |
---|
518 | | - "chrg_se1b", "se0conn", "rvrs_mode", "chrgcurr1", "vbusvld", |
---|
| 741 | + "chrg_se1b", "se0conn", "rvrs_mode", "chrgcurr2", "chrgcurr1", "vbusvld", |
---|
519 | 742 | |
---|
520 | 743 | /* REG_INT_3 */ |
---|
521 | 744 | "battdetb", |
---|
.. | .. |
---|
545 | 768 | if (IS_ERR(ddata->gpio[i])) { |
---|
546 | 769 | dev_info(ddata->dev, "no mode change GPIO%i: %li\n", |
---|
547 | 770 | i, PTR_ERR(ddata->gpio[i])); |
---|
548 | | - ddata->gpio[i] = NULL; |
---|
| 771 | + ddata->gpio[i] = NULL; |
---|
549 | 772 | } |
---|
550 | 773 | } |
---|
551 | 774 | } |
---|
.. | .. |
---|
574 | 797 | return 0; |
---|
575 | 798 | |
---|
576 | 799 | out_err: |
---|
577 | | - dev_err(ddata->dev, "could not initialize VBUS or ID IIO: %i\n", |
---|
578 | | - error); |
---|
| 800 | + if (error != -EPROBE_DEFER) |
---|
| 801 | + dev_err(ddata->dev, "could not initialize VBUS or ID IIO: %i\n", |
---|
| 802 | + error); |
---|
579 | 803 | |
---|
580 | 804 | return error; |
---|
581 | 805 | } |
---|
.. | .. |
---|
586 | 810 | .properties = cpcap_charger_props, |
---|
587 | 811 | .num_properties = ARRAY_SIZE(cpcap_charger_props), |
---|
588 | 812 | .get_property = cpcap_charger_get_property, |
---|
| 813 | + .set_property = cpcap_charger_set_property, |
---|
| 814 | + .property_is_writeable = cpcap_charger_property_is_writeable, |
---|
589 | 815 | }; |
---|
590 | 816 | |
---|
591 | 817 | #ifdef CONFIG_OF |
---|
.. | .. |
---|
615 | 841 | return -ENOMEM; |
---|
616 | 842 | |
---|
617 | 843 | ddata->dev = &pdev->dev; |
---|
| 844 | + ddata->voltage = 4200000; |
---|
618 | 845 | |
---|
619 | 846 | ddata->reg = dev_get_regmap(ddata->dev->parent, NULL); |
---|
620 | 847 | if (!ddata->reg) |
---|