| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Copyright (C) 2011 Samsung Electronics Co., Ltd. |
|---|
| 3 | 4 | * MyungJoo Ham <myungjoo.ham@samsung.com> |
|---|
| 4 | 5 | * |
|---|
| 5 | 6 | * This driver enables to monitor battery health and control charger |
|---|
| 6 | 7 | * during suspend-to-mem. |
|---|
| 7 | | - * Charger manager depends on other devices. register this later than |
|---|
| 8 | + * Charger manager depends on other devices. Register this later than |
|---|
| 8 | 9 | * the depending devices. |
|---|
| 9 | 10 | * |
|---|
| 10 | | - * This program is free software; you can redistribute it and/or modify |
|---|
| 11 | | - * it under the terms of the GNU General Public License version 2 as |
|---|
| 12 | | - * published by the Free Software Foundation. |
|---|
| 13 | 11 | **/ |
|---|
| 14 | 12 | |
|---|
| 15 | 13 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
|---|
| .. | .. |
|---|
| 28 | 26 | #include <linux/of.h> |
|---|
| 29 | 27 | #include <linux/thermal.h> |
|---|
| 30 | 28 | |
|---|
| 29 | +static struct { |
|---|
| 30 | + const char *name; |
|---|
| 31 | + u64 extcon_type; |
|---|
| 32 | +} extcon_mapping[] = { |
|---|
| 33 | + /* Current textual representations */ |
|---|
| 34 | + { "USB", EXTCON_USB }, |
|---|
| 35 | + { "USB-HOST", EXTCON_USB_HOST }, |
|---|
| 36 | + { "SDP", EXTCON_CHG_USB_SDP }, |
|---|
| 37 | + { "DCP", EXTCON_CHG_USB_DCP }, |
|---|
| 38 | + { "CDP", EXTCON_CHG_USB_CDP }, |
|---|
| 39 | + { "ACA", EXTCON_CHG_USB_ACA }, |
|---|
| 40 | + { "FAST-CHARGER", EXTCON_CHG_USB_FAST }, |
|---|
| 41 | + { "SLOW-CHARGER", EXTCON_CHG_USB_SLOW }, |
|---|
| 42 | + { "WPT", EXTCON_CHG_WPT }, |
|---|
| 43 | + { "PD", EXTCON_CHG_USB_PD }, |
|---|
| 44 | + { "DOCK", EXTCON_DOCK }, |
|---|
| 45 | + { "JIG", EXTCON_JIG }, |
|---|
| 46 | + { "MECHANICAL", EXTCON_MECHANICAL }, |
|---|
| 47 | + /* Deprecated textual representations */ |
|---|
| 48 | + { "TA", EXTCON_CHG_USB_SDP }, |
|---|
| 49 | + { "CHARGE-DOWNSTREAM", EXTCON_CHG_USB_CDP }, |
|---|
| 50 | +}; |
|---|
| 51 | + |
|---|
| 31 | 52 | /* |
|---|
| 32 | | - * Default termperature threshold for charging. |
|---|
| 53 | + * Default temperature threshold for charging. |
|---|
| 33 | 54 | * Every temperature units are in tenth of centigrade. |
|---|
| 34 | 55 | */ |
|---|
| 35 | 56 | #define CM_DEFAULT_RECHARGE_TEMP_DIFF 50 |
|---|
| 36 | 57 | #define CM_DEFAULT_CHARGE_TEMP_MAX 500 |
|---|
| 37 | | - |
|---|
| 38 | | -static const char * const default_event_names[] = { |
|---|
| 39 | | - [CM_EVENT_UNKNOWN] = "Unknown", |
|---|
| 40 | | - [CM_EVENT_BATT_FULL] = "Battery Full", |
|---|
| 41 | | - [CM_EVENT_BATT_IN] = "Battery Inserted", |
|---|
| 42 | | - [CM_EVENT_BATT_OUT] = "Battery Pulled Out", |
|---|
| 43 | | - [CM_EVENT_BATT_OVERHEAT] = "Battery Overheat", |
|---|
| 44 | | - [CM_EVENT_BATT_COLD] = "Battery Cold", |
|---|
| 45 | | - [CM_EVENT_EXT_PWR_IN_OUT] = "External Power Attach/Detach", |
|---|
| 46 | | - [CM_EVENT_CHG_START_STOP] = "Charging Start/Stop", |
|---|
| 47 | | - [CM_EVENT_OTHERS] = "Other battery events" |
|---|
| 48 | | -}; |
|---|
| 49 | 58 | |
|---|
| 50 | 59 | /* |
|---|
| 51 | 60 | * Regard CM_JIFFIES_SMALL jiffies is small enough to ignore for |
|---|
| .. | .. |
|---|
| 62 | 71 | * rtc alarm. It should be 2 or larger |
|---|
| 63 | 72 | */ |
|---|
| 64 | 73 | #define CM_RTC_SMALL (2) |
|---|
| 65 | | - |
|---|
| 66 | | -#define UEVENT_BUF_SIZE 32 |
|---|
| 67 | 74 | |
|---|
| 68 | 75 | static LIST_HEAD(cm_list); |
|---|
| 69 | 76 | static DEFINE_MUTEX(cm_list_mtx); |
|---|
| .. | .. |
|---|
| 287 | 294 | if (!fuel_gauge) |
|---|
| 288 | 295 | return false; |
|---|
| 289 | 296 | |
|---|
| 297 | + /* Full, if it's over the fullbatt voltage */ |
|---|
| 298 | + if (desc->fullbatt_uV > 0) { |
|---|
| 299 | + ret = get_batt_uV(cm, &uV); |
|---|
| 300 | + if (!ret) { |
|---|
| 301 | + /* Battery is already full, checks voltage drop. */ |
|---|
| 302 | + if (cm->battery_status == POWER_SUPPLY_STATUS_FULL |
|---|
| 303 | + && desc->fullbatt_vchkdrop_uV) |
|---|
| 304 | + uV += desc->fullbatt_vchkdrop_uV; |
|---|
| 305 | + if (uV >= desc->fullbatt_uV) |
|---|
| 306 | + return true; |
|---|
| 307 | + } |
|---|
| 308 | + } |
|---|
| 309 | + |
|---|
| 290 | 310 | if (desc->fullbatt_full_capacity > 0) { |
|---|
| 291 | 311 | val.intval = 0; |
|---|
| 292 | 312 | |
|---|
| .. | .. |
|---|
| 294 | 314 | ret = power_supply_get_property(fuel_gauge, |
|---|
| 295 | 315 | POWER_SUPPLY_PROP_CHARGE_FULL, &val); |
|---|
| 296 | 316 | if (!ret && val.intval > desc->fullbatt_full_capacity) { |
|---|
| 297 | | - is_full = true; |
|---|
| 298 | | - goto out; |
|---|
| 299 | | - } |
|---|
| 300 | | - } |
|---|
| 301 | | - |
|---|
| 302 | | - /* Full, if it's over the fullbatt voltage */ |
|---|
| 303 | | - if (desc->fullbatt_uV > 0) { |
|---|
| 304 | | - ret = get_batt_uV(cm, &uV); |
|---|
| 305 | | - if (!ret && uV >= desc->fullbatt_uV) { |
|---|
| 306 | 317 | is_full = true; |
|---|
| 307 | 318 | goto out; |
|---|
| 308 | 319 | } |
|---|
| .. | .. |
|---|
| 356 | 367 | * Note that Charger Manager keeps the charger enabled regardless whether |
|---|
| 357 | 368 | * the charger is charging or not (because battery is full or no external |
|---|
| 358 | 369 | * power source exists) except when CM needs to disable chargers forcibly |
|---|
| 359 | | - * bacause of emergency causes; when the battery is overheated or too cold. |
|---|
| 370 | + * because of emergency causes; when the battery is overheated or too cold. |
|---|
| 360 | 371 | */ |
|---|
| 361 | 372 | static int try_charger_enable(struct charger_manager *cm, bool enable) |
|---|
| 362 | 373 | { |
|---|
| 363 | 374 | int err = 0, i; |
|---|
| 364 | 375 | struct charger_desc *desc = cm->desc; |
|---|
| 365 | 376 | |
|---|
| 366 | | - /* Ignore if it's redundent command */ |
|---|
| 377 | + /* Ignore if it's redundant command */ |
|---|
| 367 | 378 | if (enable == cm->charger_enabled) |
|---|
| 368 | 379 | return 0; |
|---|
| 369 | 380 | |
|---|
| .. | .. |
|---|
| 429 | 440 | } |
|---|
| 430 | 441 | |
|---|
| 431 | 442 | /** |
|---|
| 432 | | - * try_charger_restart - Restart charging. |
|---|
| 433 | | - * @cm: the Charger Manager representing the battery. |
|---|
| 434 | | - * |
|---|
| 435 | | - * Restart charging by turning off and on the charger. |
|---|
| 436 | | - */ |
|---|
| 437 | | -static int try_charger_restart(struct charger_manager *cm) |
|---|
| 438 | | -{ |
|---|
| 439 | | - int err; |
|---|
| 440 | | - |
|---|
| 441 | | - if (cm->emergency_stop) |
|---|
| 442 | | - return -EAGAIN; |
|---|
| 443 | | - |
|---|
| 444 | | - err = try_charger_enable(cm, false); |
|---|
| 445 | | - if (err) |
|---|
| 446 | | - return err; |
|---|
| 447 | | - |
|---|
| 448 | | - return try_charger_enable(cm, true); |
|---|
| 449 | | -} |
|---|
| 450 | | - |
|---|
| 451 | | -/** |
|---|
| 452 | | - * uevent_notify - Let users know something has changed. |
|---|
| 453 | | - * @cm: the Charger Manager representing the battery. |
|---|
| 454 | | - * @event: the event string. |
|---|
| 455 | | - * |
|---|
| 456 | | - * If @event is null, it implies that uevent_notify is called |
|---|
| 457 | | - * by resume function. When called in the resume function, cm_suspended |
|---|
| 458 | | - * should be already reset to false in order to let uevent_notify |
|---|
| 459 | | - * notify the recent event during the suspend to users. While |
|---|
| 460 | | - * suspended, uevent_notify does not notify users, but tracks |
|---|
| 461 | | - * events so that uevent_notify can notify users later after resumed. |
|---|
| 462 | | - */ |
|---|
| 463 | | -static void uevent_notify(struct charger_manager *cm, const char *event) |
|---|
| 464 | | -{ |
|---|
| 465 | | - static char env_str[UEVENT_BUF_SIZE + 1] = ""; |
|---|
| 466 | | - static char env_str_save[UEVENT_BUF_SIZE + 1] = ""; |
|---|
| 467 | | - |
|---|
| 468 | | - if (cm_suspended) { |
|---|
| 469 | | - /* Nothing in suspended-event buffer */ |
|---|
| 470 | | - if (env_str_save[0] == 0) { |
|---|
| 471 | | - if (!strncmp(env_str, event, UEVENT_BUF_SIZE)) |
|---|
| 472 | | - return; /* status not changed */ |
|---|
| 473 | | - strncpy(env_str_save, event, UEVENT_BUF_SIZE); |
|---|
| 474 | | - return; |
|---|
| 475 | | - } |
|---|
| 476 | | - |
|---|
| 477 | | - if (!strncmp(env_str_save, event, UEVENT_BUF_SIZE)) |
|---|
| 478 | | - return; /* Duplicated. */ |
|---|
| 479 | | - strncpy(env_str_save, event, UEVENT_BUF_SIZE); |
|---|
| 480 | | - return; |
|---|
| 481 | | - } |
|---|
| 482 | | - |
|---|
| 483 | | - if (event == NULL) { |
|---|
| 484 | | - /* No messages pending */ |
|---|
| 485 | | - if (!env_str_save[0]) |
|---|
| 486 | | - return; |
|---|
| 487 | | - |
|---|
| 488 | | - strncpy(env_str, env_str_save, UEVENT_BUF_SIZE); |
|---|
| 489 | | - kobject_uevent(&cm->dev->kobj, KOBJ_CHANGE); |
|---|
| 490 | | - env_str_save[0] = 0; |
|---|
| 491 | | - |
|---|
| 492 | | - return; |
|---|
| 493 | | - } |
|---|
| 494 | | - |
|---|
| 495 | | - /* status not changed */ |
|---|
| 496 | | - if (!strncmp(env_str, event, UEVENT_BUF_SIZE)) |
|---|
| 497 | | - return; |
|---|
| 498 | | - |
|---|
| 499 | | - /* save the status and notify the update */ |
|---|
| 500 | | - strncpy(env_str, event, UEVENT_BUF_SIZE); |
|---|
| 501 | | - kobject_uevent(&cm->dev->kobj, KOBJ_CHANGE); |
|---|
| 502 | | - |
|---|
| 503 | | - dev_info(cm->dev, "%s\n", event); |
|---|
| 504 | | -} |
|---|
| 505 | | - |
|---|
| 506 | | -/** |
|---|
| 507 | | - * fullbatt_vchk - Check voltage drop some times after "FULL" event. |
|---|
| 508 | | - * @work: the work_struct appointing the function |
|---|
| 509 | | - * |
|---|
| 510 | | - * If a user has designated "fullbatt_vchkdrop_ms/uV" values with |
|---|
| 511 | | - * charger_desc, Charger Manager checks voltage drop after the battery |
|---|
| 512 | | - * "FULL" event. It checks whether the voltage has dropped more than |
|---|
| 513 | | - * fullbatt_vchkdrop_uV by calling this function after fullbatt_vchkrop_ms. |
|---|
| 514 | | - */ |
|---|
| 515 | | -static void fullbatt_vchk(struct work_struct *work) |
|---|
| 516 | | -{ |
|---|
| 517 | | - struct delayed_work *dwork = to_delayed_work(work); |
|---|
| 518 | | - struct charger_manager *cm = container_of(dwork, |
|---|
| 519 | | - struct charger_manager, fullbatt_vchk_work); |
|---|
| 520 | | - struct charger_desc *desc = cm->desc; |
|---|
| 521 | | - int batt_uV, err, diff; |
|---|
| 522 | | - |
|---|
| 523 | | - /* remove the appointment for fullbatt_vchk */ |
|---|
| 524 | | - cm->fullbatt_vchk_jiffies_at = 0; |
|---|
| 525 | | - |
|---|
| 526 | | - if (!desc->fullbatt_vchkdrop_uV || !desc->fullbatt_vchkdrop_ms) |
|---|
| 527 | | - return; |
|---|
| 528 | | - |
|---|
| 529 | | - err = get_batt_uV(cm, &batt_uV); |
|---|
| 530 | | - if (err) { |
|---|
| 531 | | - dev_err(cm->dev, "%s: get_batt_uV error(%d)\n", __func__, err); |
|---|
| 532 | | - return; |
|---|
| 533 | | - } |
|---|
| 534 | | - |
|---|
| 535 | | - diff = desc->fullbatt_uV - batt_uV; |
|---|
| 536 | | - if (diff < 0) |
|---|
| 537 | | - return; |
|---|
| 538 | | - |
|---|
| 539 | | - dev_info(cm->dev, "VBATT dropped %duV after full-batt\n", diff); |
|---|
| 540 | | - |
|---|
| 541 | | - if (diff > desc->fullbatt_vchkdrop_uV) { |
|---|
| 542 | | - try_charger_restart(cm); |
|---|
| 543 | | - uevent_notify(cm, "Recharging"); |
|---|
| 544 | | - } |
|---|
| 545 | | -} |
|---|
| 546 | | - |
|---|
| 547 | | -/** |
|---|
| 548 | 443 | * check_charging_duration - Monitor charging/discharging duration |
|---|
| 549 | 444 | * @cm: the Charger Manager representing the battery. |
|---|
| 550 | 445 | * |
|---|
| .. | .. |
|---|
| 571 | 466 | if (duration > desc->charging_max_duration_ms) { |
|---|
| 572 | 467 | dev_info(cm->dev, "Charging duration exceed %ums\n", |
|---|
| 573 | 468 | desc->charging_max_duration_ms); |
|---|
| 574 | | - uevent_notify(cm, "Discharging"); |
|---|
| 575 | | - try_charger_enable(cm, false); |
|---|
| 576 | 469 | ret = true; |
|---|
| 577 | 470 | } |
|---|
| 578 | | - } else if (is_ext_pwr_online(cm) && !cm->charger_enabled) { |
|---|
| 471 | + } else if (cm->battery_status == POWER_SUPPLY_STATUS_NOT_CHARGING) { |
|---|
| 579 | 472 | duration = curr - cm->charging_end_time; |
|---|
| 580 | 473 | |
|---|
| 581 | | - if (duration > desc->discharging_max_duration_ms && |
|---|
| 582 | | - is_ext_pwr_online(cm)) { |
|---|
| 474 | + if (duration > desc->discharging_max_duration_ms) { |
|---|
| 583 | 475 | dev_info(cm->dev, "Discharging duration exceed %ums\n", |
|---|
| 584 | 476 | desc->discharging_max_duration_ms); |
|---|
| 585 | | - uevent_notify(cm, "Recharging"); |
|---|
| 586 | | - try_charger_enable(cm, true); |
|---|
| 587 | 477 | ret = true; |
|---|
| 588 | 478 | } |
|---|
| 589 | 479 | } |
|---|
| .. | .. |
|---|
| 643 | 533 | if (ret) { |
|---|
| 644 | 534 | /* FIXME: |
|---|
| 645 | 535 | * No information of battery temperature might |
|---|
| 646 | | - * occur hazadous result. We have to handle it |
|---|
| 536 | + * occur hazardous result. We have to handle it |
|---|
| 647 | 537 | * depending on battery type. |
|---|
| 648 | 538 | */ |
|---|
| 649 | 539 | dev_err(cm->dev, "Failed to get battery temperature\n"); |
|---|
| .. | .. |
|---|
| 659 | 549 | } |
|---|
| 660 | 550 | |
|---|
| 661 | 551 | if (temp > upper_limit) |
|---|
| 662 | | - ret = CM_EVENT_BATT_OVERHEAT; |
|---|
| 552 | + ret = CM_BATT_OVERHEAT; |
|---|
| 663 | 553 | else if (temp < lower_limit) |
|---|
| 664 | | - ret = CM_EVENT_BATT_COLD; |
|---|
| 554 | + ret = CM_BATT_COLD; |
|---|
| 555 | + else |
|---|
| 556 | + ret = CM_BATT_OK; |
|---|
| 557 | + |
|---|
| 558 | + cm->emergency_stop = ret; |
|---|
| 665 | 559 | |
|---|
| 666 | 560 | return ret; |
|---|
| 561 | +} |
|---|
| 562 | + |
|---|
| 563 | +/** |
|---|
| 564 | + * cm_get_target_status - Check current status and get next target status. |
|---|
| 565 | + * @cm: the Charger Manager representing the battery. |
|---|
| 566 | + */ |
|---|
| 567 | +static int cm_get_target_status(struct charger_manager *cm) |
|---|
| 568 | +{ |
|---|
| 569 | + if (!is_ext_pwr_online(cm)) |
|---|
| 570 | + return POWER_SUPPLY_STATUS_DISCHARGING; |
|---|
| 571 | + |
|---|
| 572 | + if (cm_check_thermal_status(cm)) { |
|---|
| 573 | + /* Check if discharging duration exeeds limit. */ |
|---|
| 574 | + if (check_charging_duration(cm)) |
|---|
| 575 | + goto charging_ok; |
|---|
| 576 | + return POWER_SUPPLY_STATUS_NOT_CHARGING; |
|---|
| 577 | + } |
|---|
| 578 | + |
|---|
| 579 | + switch (cm->battery_status) { |
|---|
| 580 | + case POWER_SUPPLY_STATUS_CHARGING: |
|---|
| 581 | + /* Check if charging duration exeeds limit. */ |
|---|
| 582 | + if (check_charging_duration(cm)) |
|---|
| 583 | + return POWER_SUPPLY_STATUS_FULL; |
|---|
| 584 | + fallthrough; |
|---|
| 585 | + case POWER_SUPPLY_STATUS_FULL: |
|---|
| 586 | + if (is_full_charged(cm)) |
|---|
| 587 | + return POWER_SUPPLY_STATUS_FULL; |
|---|
| 588 | + fallthrough; |
|---|
| 589 | + default: |
|---|
| 590 | + break; |
|---|
| 591 | + } |
|---|
| 592 | + |
|---|
| 593 | +charging_ok: |
|---|
| 594 | + /* Charging is allowed. */ |
|---|
| 595 | + return POWER_SUPPLY_STATUS_CHARGING; |
|---|
| 667 | 596 | } |
|---|
| 668 | 597 | |
|---|
| 669 | 598 | /** |
|---|
| .. | .. |
|---|
| 675 | 604 | */ |
|---|
| 676 | 605 | static bool _cm_monitor(struct charger_manager *cm) |
|---|
| 677 | 606 | { |
|---|
| 678 | | - int temp_alrt; |
|---|
| 607 | + int target; |
|---|
| 679 | 608 | |
|---|
| 680 | | - temp_alrt = cm_check_thermal_status(cm); |
|---|
| 609 | + target = cm_get_target_status(cm); |
|---|
| 681 | 610 | |
|---|
| 682 | | - /* It has been stopped already */ |
|---|
| 683 | | - if (temp_alrt && cm->emergency_stop) |
|---|
| 684 | | - return false; |
|---|
| 611 | + try_charger_enable(cm, (target == POWER_SUPPLY_STATUS_CHARGING)); |
|---|
| 685 | 612 | |
|---|
| 686 | | - /* |
|---|
| 687 | | - * Check temperature whether overheat or cold. |
|---|
| 688 | | - * If temperature is out of range normal state, stop charging. |
|---|
| 689 | | - */ |
|---|
| 690 | | - if (temp_alrt) { |
|---|
| 691 | | - cm->emergency_stop = temp_alrt; |
|---|
| 692 | | - if (!try_charger_enable(cm, false)) |
|---|
| 693 | | - uevent_notify(cm, default_event_names[temp_alrt]); |
|---|
| 694 | | - |
|---|
| 695 | | - /* |
|---|
| 696 | | - * Check whole charging duration and discharing duration |
|---|
| 697 | | - * after full-batt. |
|---|
| 698 | | - */ |
|---|
| 699 | | - } else if (!cm->emergency_stop && check_charging_duration(cm)) { |
|---|
| 700 | | - dev_dbg(cm->dev, |
|---|
| 701 | | - "Charging/Discharging duration is out of range\n"); |
|---|
| 702 | | - /* |
|---|
| 703 | | - * Check dropped voltage of battery. If battery voltage is more |
|---|
| 704 | | - * dropped than fullbatt_vchkdrop_uV after fully charged state, |
|---|
| 705 | | - * charger-manager have to recharge battery. |
|---|
| 706 | | - */ |
|---|
| 707 | | - } else if (!cm->emergency_stop && is_ext_pwr_online(cm) && |
|---|
| 708 | | - !cm->charger_enabled) { |
|---|
| 709 | | - fullbatt_vchk(&cm->fullbatt_vchk_work.work); |
|---|
| 710 | | - |
|---|
| 711 | | - /* |
|---|
| 712 | | - * Check whether fully charged state to protect overcharge |
|---|
| 713 | | - * if charger-manager is charging for battery. |
|---|
| 714 | | - */ |
|---|
| 715 | | - } else if (!cm->emergency_stop && is_full_charged(cm) && |
|---|
| 716 | | - cm->charger_enabled) { |
|---|
| 717 | | - dev_info(cm->dev, "EVENT_HANDLE: Battery Fully Charged\n"); |
|---|
| 718 | | - uevent_notify(cm, default_event_names[CM_EVENT_BATT_FULL]); |
|---|
| 719 | | - |
|---|
| 720 | | - try_charger_enable(cm, false); |
|---|
| 721 | | - |
|---|
| 722 | | - fullbatt_vchk(&cm->fullbatt_vchk_work.work); |
|---|
| 723 | | - } else { |
|---|
| 724 | | - cm->emergency_stop = 0; |
|---|
| 725 | | - if (is_ext_pwr_online(cm)) { |
|---|
| 726 | | - if (!try_charger_enable(cm, true)) |
|---|
| 727 | | - uevent_notify(cm, "CHARGING"); |
|---|
| 728 | | - } |
|---|
| 613 | + if (cm->battery_status != target) { |
|---|
| 614 | + cm->battery_status = target; |
|---|
| 615 | + power_supply_changed(cm->charger_psy); |
|---|
| 729 | 616 | } |
|---|
| 730 | 617 | |
|---|
| 731 | | - return true; |
|---|
| 618 | + return (cm->battery_status == POWER_SUPPLY_STATUS_NOT_CHARGING); |
|---|
| 732 | 619 | } |
|---|
| 733 | 620 | |
|---|
| 734 | 621 | /** |
|---|
| .. | .. |
|---|
| 821 | 708 | schedule_work(&setup_polling); |
|---|
| 822 | 709 | } |
|---|
| 823 | 710 | |
|---|
| 824 | | -/** |
|---|
| 825 | | - * fullbatt_handler - Event handler for CM_EVENT_BATT_FULL |
|---|
| 826 | | - * @cm: the Charger Manager representing the battery. |
|---|
| 827 | | - */ |
|---|
| 828 | | -static void fullbatt_handler(struct charger_manager *cm) |
|---|
| 829 | | -{ |
|---|
| 830 | | - struct charger_desc *desc = cm->desc; |
|---|
| 831 | | - |
|---|
| 832 | | - if (!desc->fullbatt_vchkdrop_uV || !desc->fullbatt_vchkdrop_ms) |
|---|
| 833 | | - goto out; |
|---|
| 834 | | - |
|---|
| 835 | | - if (cm_suspended) |
|---|
| 836 | | - device_set_wakeup_capable(cm->dev, true); |
|---|
| 837 | | - |
|---|
| 838 | | - mod_delayed_work(cm_wq, &cm->fullbatt_vchk_work, |
|---|
| 839 | | - msecs_to_jiffies(desc->fullbatt_vchkdrop_ms)); |
|---|
| 840 | | - cm->fullbatt_vchk_jiffies_at = jiffies + msecs_to_jiffies( |
|---|
| 841 | | - desc->fullbatt_vchkdrop_ms); |
|---|
| 842 | | - |
|---|
| 843 | | - if (cm->fullbatt_vchk_jiffies_at == 0) |
|---|
| 844 | | - cm->fullbatt_vchk_jiffies_at = 1; |
|---|
| 845 | | - |
|---|
| 846 | | -out: |
|---|
| 847 | | - dev_info(cm->dev, "EVENT_HANDLE: Battery Fully Charged\n"); |
|---|
| 848 | | - uevent_notify(cm, default_event_names[CM_EVENT_BATT_FULL]); |
|---|
| 849 | | -} |
|---|
| 850 | | - |
|---|
| 851 | | -/** |
|---|
| 852 | | - * battout_handler - Event handler for CM_EVENT_BATT_OUT |
|---|
| 853 | | - * @cm: the Charger Manager representing the battery. |
|---|
| 854 | | - */ |
|---|
| 855 | | -static void battout_handler(struct charger_manager *cm) |
|---|
| 856 | | -{ |
|---|
| 857 | | - if (cm_suspended) |
|---|
| 858 | | - device_set_wakeup_capable(cm->dev, true); |
|---|
| 859 | | - |
|---|
| 860 | | - if (!is_batt_present(cm)) { |
|---|
| 861 | | - dev_emerg(cm->dev, "Battery Pulled Out!\n"); |
|---|
| 862 | | - uevent_notify(cm, default_event_names[CM_EVENT_BATT_OUT]); |
|---|
| 863 | | - } else { |
|---|
| 864 | | - uevent_notify(cm, "Battery Reinserted?"); |
|---|
| 865 | | - } |
|---|
| 866 | | -} |
|---|
| 867 | | - |
|---|
| 868 | | -/** |
|---|
| 869 | | - * misc_event_handler - Handler for other evnets |
|---|
| 870 | | - * @cm: the Charger Manager representing the battery. |
|---|
| 871 | | - * @type: the Charger Manager representing the battery. |
|---|
| 872 | | - */ |
|---|
| 873 | | -static void misc_event_handler(struct charger_manager *cm, |
|---|
| 874 | | - enum cm_event_types type) |
|---|
| 875 | | -{ |
|---|
| 876 | | - if (cm_suspended) |
|---|
| 877 | | - device_set_wakeup_capable(cm->dev, true); |
|---|
| 878 | | - |
|---|
| 879 | | - if (is_polling_required(cm) && cm->desc->polling_interval_ms) |
|---|
| 880 | | - schedule_work(&setup_polling); |
|---|
| 881 | | - uevent_notify(cm, default_event_names[type]); |
|---|
| 882 | | -} |
|---|
| 883 | | - |
|---|
| 884 | 711 | static int charger_get_property(struct power_supply *psy, |
|---|
| 885 | 712 | enum power_supply_property psp, |
|---|
| 886 | 713 | union power_supply_propval *val) |
|---|
| .. | .. |
|---|
| 893 | 720 | |
|---|
| 894 | 721 | switch (psp) { |
|---|
| 895 | 722 | case POWER_SUPPLY_PROP_STATUS: |
|---|
| 896 | | - if (is_charging(cm)) |
|---|
| 897 | | - val->intval = POWER_SUPPLY_STATUS_CHARGING; |
|---|
| 898 | | - else if (is_ext_pwr_online(cm)) |
|---|
| 899 | | - val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; |
|---|
| 900 | | - else |
|---|
| 901 | | - val->intval = POWER_SUPPLY_STATUS_DISCHARGING; |
|---|
| 723 | + val->intval = cm->battery_status; |
|---|
| 902 | 724 | break; |
|---|
| 903 | 725 | case POWER_SUPPLY_PROP_HEALTH: |
|---|
| 904 | 726 | if (cm->emergency_stop > 0) |
|---|
| .. | .. |
|---|
| 927 | 749 | POWER_SUPPLY_PROP_CURRENT_NOW, val); |
|---|
| 928 | 750 | break; |
|---|
| 929 | 751 | case POWER_SUPPLY_PROP_TEMP: |
|---|
| 930 | | - case POWER_SUPPLY_PROP_TEMP_AMBIENT: |
|---|
| 931 | 752 | return cm_get_battery_temperature(cm, &val->intval); |
|---|
| 932 | 753 | case POWER_SUPPLY_PROP_CAPACITY: |
|---|
| 933 | 754 | if (!is_batt_present(cm)) { |
|---|
| .. | .. |
|---|
| 983 | 804 | val->intval = 0; |
|---|
| 984 | 805 | break; |
|---|
| 985 | 806 | case POWER_SUPPLY_PROP_CHARGE_FULL: |
|---|
| 986 | | - if (is_full_charged(cm)) |
|---|
| 987 | | - val->intval = 1; |
|---|
| 988 | | - else |
|---|
| 989 | | - val->intval = 0; |
|---|
| 990 | | - ret = 0; |
|---|
| 991 | | - break; |
|---|
| 992 | 807 | case POWER_SUPPLY_PROP_CHARGE_NOW: |
|---|
| 993 | | - if (is_charging(cm)) { |
|---|
| 994 | | - fuel_gauge = power_supply_get_by_name( |
|---|
| 995 | | - cm->desc->psy_fuel_gauge); |
|---|
| 996 | | - if (!fuel_gauge) { |
|---|
| 997 | | - ret = -ENODEV; |
|---|
| 998 | | - break; |
|---|
| 999 | | - } |
|---|
| 1000 | | - |
|---|
| 1001 | | - ret = power_supply_get_property(fuel_gauge, |
|---|
| 1002 | | - POWER_SUPPLY_PROP_CHARGE_NOW, |
|---|
| 1003 | | - val); |
|---|
| 1004 | | - if (ret) { |
|---|
| 1005 | | - val->intval = 1; |
|---|
| 1006 | | - ret = 0; |
|---|
| 1007 | | - } else { |
|---|
| 1008 | | - /* If CHARGE_NOW is supplied, use it */ |
|---|
| 1009 | | - val->intval = (val->intval > 0) ? |
|---|
| 1010 | | - val->intval : 1; |
|---|
| 1011 | | - } |
|---|
| 1012 | | - } else { |
|---|
| 1013 | | - val->intval = 0; |
|---|
| 808 | + fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge); |
|---|
| 809 | + if (!fuel_gauge) { |
|---|
| 810 | + ret = -ENODEV; |
|---|
| 811 | + break; |
|---|
| 1014 | 812 | } |
|---|
| 813 | + ret = power_supply_get_property(fuel_gauge, psp, val); |
|---|
| 1015 | 814 | break; |
|---|
| 1016 | 815 | default: |
|---|
| 1017 | 816 | return -EINVAL; |
|---|
| .. | .. |
|---|
| 1030 | 829 | POWER_SUPPLY_PROP_VOLTAGE_NOW, |
|---|
| 1031 | 830 | POWER_SUPPLY_PROP_CAPACITY, |
|---|
| 1032 | 831 | POWER_SUPPLY_PROP_ONLINE, |
|---|
| 1033 | | - POWER_SUPPLY_PROP_CHARGE_FULL, |
|---|
| 1034 | 832 | /* |
|---|
| 1035 | 833 | * Optional properties are: |
|---|
| 834 | + * POWER_SUPPLY_PROP_CHARGE_FULL, |
|---|
| 1036 | 835 | * POWER_SUPPLY_PROP_CHARGE_NOW, |
|---|
| 1037 | 836 | * POWER_SUPPLY_PROP_CURRENT_NOW, |
|---|
| 1038 | | - * POWER_SUPPLY_PROP_TEMP, and |
|---|
| 1039 | | - * POWER_SUPPLY_PROP_TEMP_AMBIENT, |
|---|
| 837 | + * POWER_SUPPLY_PROP_TEMP, |
|---|
| 1040 | 838 | */ |
|---|
| 1041 | 839 | }; |
|---|
| 1042 | 840 | |
|---|
| .. | .. |
|---|
| 1071 | 869 | |
|---|
| 1072 | 870 | mutex_lock(&cm_list_mtx); |
|---|
| 1073 | 871 | list_for_each_entry(cm, &cm_list, entry) { |
|---|
| 1074 | | - unsigned int fbchk_ms = 0; |
|---|
| 1075 | | - |
|---|
| 1076 | | - /* fullbatt_vchk is required. setup timer for that */ |
|---|
| 1077 | | - if (cm->fullbatt_vchk_jiffies_at) { |
|---|
| 1078 | | - fbchk_ms = jiffies_to_msecs(cm->fullbatt_vchk_jiffies_at |
|---|
| 1079 | | - - jiffies); |
|---|
| 1080 | | - if (time_is_before_eq_jiffies( |
|---|
| 1081 | | - cm->fullbatt_vchk_jiffies_at) || |
|---|
| 1082 | | - msecs_to_jiffies(fbchk_ms) < CM_JIFFIES_SMALL) { |
|---|
| 1083 | | - fullbatt_vchk(&cm->fullbatt_vchk_work.work); |
|---|
| 1084 | | - fbchk_ms = 0; |
|---|
| 1085 | | - } |
|---|
| 1086 | | - } |
|---|
| 1087 | | - CM_MIN_VALID(wakeup_ms, fbchk_ms); |
|---|
| 1088 | | - |
|---|
| 1089 | 872 | /* Skip if polling is not required for this CM */ |
|---|
| 1090 | 873 | if (!is_polling_required(cm) && !cm->emergency_stop) |
|---|
| 1091 | 874 | continue; |
|---|
| .. | .. |
|---|
| 1147 | 930 | cable->min_uA, cable->max_uA); |
|---|
| 1148 | 931 | } |
|---|
| 1149 | 932 | |
|---|
| 1150 | | - try_charger_enable(cable->cm, cable->attached); |
|---|
| 933 | + cancel_delayed_work(&cm_monitor_work); |
|---|
| 934 | + queue_delayed_work(cm_wq, &cm_monitor_work, 0); |
|---|
| 1151 | 935 | } |
|---|
| 1152 | 936 | |
|---|
| 1153 | 937 | /** |
|---|
| .. | .. |
|---|
| 1171 | 955 | cable->attached = event; |
|---|
| 1172 | 956 | |
|---|
| 1173 | 957 | /* |
|---|
| 1174 | | - * Setup monitoring to check battery state |
|---|
| 1175 | | - * when charger cable is attached. |
|---|
| 1176 | | - */ |
|---|
| 1177 | | - if (cable->attached && is_polling_required(cable->cm)) { |
|---|
| 1178 | | - cancel_work_sync(&setup_polling); |
|---|
| 1179 | | - schedule_work(&setup_polling); |
|---|
| 1180 | | - } |
|---|
| 1181 | | - |
|---|
| 1182 | | - /* |
|---|
| 1183 | 958 | * Setup work for controlling charger(regulator) |
|---|
| 1184 | 959 | * according to charger cable. |
|---|
| 1185 | 960 | */ |
|---|
| .. | .. |
|---|
| 1198 | 973 | static int charger_extcon_init(struct charger_manager *cm, |
|---|
| 1199 | 974 | struct charger_cable *cable) |
|---|
| 1200 | 975 | { |
|---|
| 1201 | | - int ret; |
|---|
| 976 | + int ret, i; |
|---|
| 977 | + u64 extcon_type = EXTCON_NONE; |
|---|
| 1202 | 978 | |
|---|
| 1203 | 979 | /* |
|---|
| 1204 | 980 | * Charger manager use Extcon framework to identify |
|---|
| .. | .. |
|---|
| 1207 | 983 | */ |
|---|
| 1208 | 984 | INIT_WORK(&cable->wq, charger_extcon_work); |
|---|
| 1209 | 985 | cable->nb.notifier_call = charger_extcon_notifier; |
|---|
| 1210 | | - ret = extcon_register_interest(&cable->extcon_dev, |
|---|
| 1211 | | - cable->extcon_name, cable->name, &cable->nb); |
|---|
| 1212 | | - if (ret < 0) { |
|---|
| 1213 | | - pr_info("Cannot register extcon_dev for %s(cable: %s)\n", |
|---|
| 986 | + |
|---|
| 987 | + cable->extcon_dev = extcon_get_extcon_dev(cable->extcon_name); |
|---|
| 988 | + if (IS_ERR_OR_NULL(cable->extcon_dev)) { |
|---|
| 989 | + pr_err("Cannot find extcon_dev for %s (cable: %s)\n", |
|---|
| 1214 | 990 | cable->extcon_name, cable->name); |
|---|
| 991 | + if (cable->extcon_dev == NULL) |
|---|
| 992 | + return -EPROBE_DEFER; |
|---|
| 993 | + else |
|---|
| 994 | + return PTR_ERR(cable->extcon_dev); |
|---|
| 1215 | 995 | } |
|---|
| 1216 | 996 | |
|---|
| 1217 | | - return ret; |
|---|
| 997 | + for (i = 0; i < ARRAY_SIZE(extcon_mapping); i++) { |
|---|
| 998 | + if (!strcmp(cable->name, extcon_mapping[i].name)) { |
|---|
| 999 | + extcon_type = extcon_mapping[i].extcon_type; |
|---|
| 1000 | + break; |
|---|
| 1001 | + } |
|---|
| 1002 | + } |
|---|
| 1003 | + if (extcon_type == EXTCON_NONE) { |
|---|
| 1004 | + pr_err("Cannot find cable for type %s", cable->name); |
|---|
| 1005 | + return -EINVAL; |
|---|
| 1006 | + } |
|---|
| 1007 | + |
|---|
| 1008 | + cable->extcon_type = extcon_type; |
|---|
| 1009 | + |
|---|
| 1010 | + ret = devm_extcon_register_notifier(cm->dev, cable->extcon_dev, |
|---|
| 1011 | + cable->extcon_type, &cable->nb); |
|---|
| 1012 | + if (ret < 0) { |
|---|
| 1013 | + pr_err("Cannot register extcon_dev for %s (cable: %s)\n", |
|---|
| 1014 | + cable->extcon_name, cable->name); |
|---|
| 1015 | + return ret; |
|---|
| 1016 | + } |
|---|
| 1017 | + |
|---|
| 1018 | + return 0; |
|---|
| 1218 | 1019 | } |
|---|
| 1219 | 1020 | |
|---|
| 1220 | 1021 | /** |
|---|
| 1221 | | - * charger_manager_register_extcon - Register extcon device to recevie state |
|---|
| 1022 | + * charger_manager_register_extcon - Register extcon device to receive state |
|---|
| 1222 | 1023 | * of charger cable. |
|---|
| 1223 | 1024 | * @cm: the Charger Manager representing the battery. |
|---|
| 1224 | 1025 | * |
|---|
| .. | .. |
|---|
| 1231 | 1032 | { |
|---|
| 1232 | 1033 | struct charger_desc *desc = cm->desc; |
|---|
| 1233 | 1034 | struct charger_regulator *charger; |
|---|
| 1035 | + unsigned long event; |
|---|
| 1234 | 1036 | int ret; |
|---|
| 1235 | 1037 | int i; |
|---|
| 1236 | 1038 | int j; |
|---|
| .. | .. |
|---|
| 1258 | 1060 | } |
|---|
| 1259 | 1061 | cable->charger = charger; |
|---|
| 1260 | 1062 | cable->cm = cm; |
|---|
| 1063 | + |
|---|
| 1064 | + event = extcon_get_state(cable->extcon_dev, |
|---|
| 1065 | + cable->extcon_type); |
|---|
| 1066 | + charger_extcon_notifier(&cable->nb, |
|---|
| 1067 | + event, NULL); |
|---|
| 1261 | 1068 | } |
|---|
| 1262 | 1069 | } |
|---|
| 1263 | 1070 | |
|---|
| .. | .. |
|---|
| 1351 | 1158 | } |
|---|
| 1352 | 1159 | |
|---|
| 1353 | 1160 | /** |
|---|
| 1354 | | - * charger_manager_register_sysfs - Register sysfs entry for each charger |
|---|
| 1161 | + * charger_manager_prepare_sysfs - Prepare sysfs entry for each charger |
|---|
| 1355 | 1162 | * @cm: the Charger Manager representing the battery. |
|---|
| 1356 | 1163 | * |
|---|
| 1357 | 1164 | * This function add sysfs entry for charger(regulator) to control charger from |
|---|
| .. | .. |
|---|
| 1363 | 1170 | * externally_control, this charger isn't controlled from charger-manager and |
|---|
| 1364 | 1171 | * always stay off state of regulator. |
|---|
| 1365 | 1172 | */ |
|---|
| 1366 | | -static int charger_manager_register_sysfs(struct charger_manager *cm) |
|---|
| 1173 | +static int charger_manager_prepare_sysfs(struct charger_manager *cm) |
|---|
| 1367 | 1174 | { |
|---|
| 1368 | 1175 | struct charger_desc *desc = cm->desc; |
|---|
| 1369 | 1176 | struct charger_regulator *charger; |
|---|
| 1370 | 1177 | int chargers_externally_control = 1; |
|---|
| 1371 | | - char buf[11]; |
|---|
| 1372 | | - char *str; |
|---|
| 1373 | | - int ret; |
|---|
| 1178 | + char *name; |
|---|
| 1374 | 1179 | int i; |
|---|
| 1375 | 1180 | |
|---|
| 1376 | 1181 | /* Create sysfs entry to control charger(regulator) */ |
|---|
| 1377 | 1182 | for (i = 0; i < desc->num_charger_regulators; i++) { |
|---|
| 1378 | 1183 | charger = &desc->charger_regulators[i]; |
|---|
| 1379 | 1184 | |
|---|
| 1380 | | - snprintf(buf, 10, "charger.%d", i); |
|---|
| 1381 | | - str = devm_kzalloc(cm->dev, |
|---|
| 1382 | | - strlen(buf) + 1, GFP_KERNEL); |
|---|
| 1383 | | - if (!str) |
|---|
| 1185 | + name = devm_kasprintf(cm->dev, GFP_KERNEL, "charger.%d", i); |
|---|
| 1186 | + if (!name) |
|---|
| 1384 | 1187 | return -ENOMEM; |
|---|
| 1385 | | - |
|---|
| 1386 | | - strcpy(str, buf); |
|---|
| 1387 | 1188 | |
|---|
| 1388 | 1189 | charger->attrs[0] = &charger->attr_name.attr; |
|---|
| 1389 | 1190 | charger->attrs[1] = &charger->attr_state.attr; |
|---|
| 1390 | 1191 | charger->attrs[2] = &charger->attr_externally_control.attr; |
|---|
| 1391 | 1192 | charger->attrs[3] = NULL; |
|---|
| 1392 | | - charger->attr_g.name = str; |
|---|
| 1393 | | - charger->attr_g.attrs = charger->attrs; |
|---|
| 1193 | + |
|---|
| 1194 | + charger->attr_grp.name = name; |
|---|
| 1195 | + charger->attr_grp.attrs = charger->attrs; |
|---|
| 1196 | + desc->sysfs_groups[i] = &charger->attr_grp; |
|---|
| 1394 | 1197 | |
|---|
| 1395 | 1198 | sysfs_attr_init(&charger->attr_name.attr); |
|---|
| 1396 | 1199 | charger->attr_name.attr.name = "name"; |
|---|
| .. | .. |
|---|
| 1417 | 1220 | |
|---|
| 1418 | 1221 | dev_info(cm->dev, "'%s' regulator's externally_control is %d\n", |
|---|
| 1419 | 1222 | charger->regulator_name, charger->externally_control); |
|---|
| 1420 | | - |
|---|
| 1421 | | - ret = sysfs_create_group(&cm->charger_psy->dev.kobj, |
|---|
| 1422 | | - &charger->attr_g); |
|---|
| 1423 | | - if (ret < 0) { |
|---|
| 1424 | | - dev_err(cm->dev, "Cannot create sysfs entry of %s regulator\n", |
|---|
| 1425 | | - charger->regulator_name); |
|---|
| 1426 | | - return ret; |
|---|
| 1427 | | - } |
|---|
| 1428 | 1223 | } |
|---|
| 1429 | 1224 | |
|---|
| 1430 | 1225 | if (chargers_externally_control) { |
|---|
| .. | .. |
|---|
| 1436 | 1231 | } |
|---|
| 1437 | 1232 | |
|---|
| 1438 | 1233 | static int cm_init_thermal_data(struct charger_manager *cm, |
|---|
| 1439 | | - struct power_supply *fuel_gauge) |
|---|
| 1234 | + struct power_supply *fuel_gauge, |
|---|
| 1235 | + enum power_supply_property *properties, |
|---|
| 1236 | + size_t *num_properties) |
|---|
| 1440 | 1237 | { |
|---|
| 1441 | 1238 | struct charger_desc *desc = cm->desc; |
|---|
| 1442 | 1239 | union power_supply_propval val; |
|---|
| .. | .. |
|---|
| 1447 | 1244 | POWER_SUPPLY_PROP_TEMP, &val); |
|---|
| 1448 | 1245 | |
|---|
| 1449 | 1246 | if (!ret) { |
|---|
| 1450 | | - cm->charger_psy_desc.properties[cm->charger_psy_desc.num_properties] = |
|---|
| 1451 | | - POWER_SUPPLY_PROP_TEMP; |
|---|
| 1452 | | - cm->charger_psy_desc.num_properties++; |
|---|
| 1247 | + properties[*num_properties] = POWER_SUPPLY_PROP_TEMP; |
|---|
| 1248 | + (*num_properties)++; |
|---|
| 1453 | 1249 | cm->desc->measure_battery_temp = true; |
|---|
| 1454 | 1250 | } |
|---|
| 1455 | 1251 | #ifdef CONFIG_THERMAL |
|---|
| .. | .. |
|---|
| 1460 | 1256 | return PTR_ERR(cm->tzd_batt); |
|---|
| 1461 | 1257 | |
|---|
| 1462 | 1258 | /* Use external thermometer */ |
|---|
| 1463 | | - cm->charger_psy_desc.properties[cm->charger_psy_desc.num_properties] = |
|---|
| 1464 | | - POWER_SUPPLY_PROP_TEMP_AMBIENT; |
|---|
| 1465 | | - cm->charger_psy_desc.num_properties++; |
|---|
| 1259 | + properties[*num_properties] = POWER_SUPPLY_PROP_TEMP; |
|---|
| 1260 | + (*num_properties)++; |
|---|
| 1466 | 1261 | cm->desc->measure_battery_temp = true; |
|---|
| 1467 | 1262 | ret = 0; |
|---|
| 1468 | 1263 | } |
|---|
| .. | .. |
|---|
| 1506 | 1301 | of_property_read_u32(np, "cm-poll-interval", |
|---|
| 1507 | 1302 | &desc->polling_interval_ms); |
|---|
| 1508 | 1303 | |
|---|
| 1509 | | - of_property_read_u32(np, "cm-fullbatt-vchkdrop-ms", |
|---|
| 1510 | | - &desc->fullbatt_vchkdrop_ms); |
|---|
| 1511 | 1304 | of_property_read_u32(np, "cm-fullbatt-vchkdrop-volt", |
|---|
| 1512 | 1305 | &desc->fullbatt_vchkdrop_uV); |
|---|
| 1513 | 1306 | of_property_read_u32(np, "cm-fullbatt-voltage", &desc->fullbatt_uV); |
|---|
| .. | .. |
|---|
| 1519 | 1312 | desc->battery_present = battery_stat; |
|---|
| 1520 | 1313 | |
|---|
| 1521 | 1314 | /* chargers */ |
|---|
| 1522 | | - of_property_read_u32(np, "cm-num-chargers", &num_chgs); |
|---|
| 1523 | | - if (num_chgs) { |
|---|
| 1315 | + num_chgs = of_property_count_strings(np, "cm-chargers"); |
|---|
| 1316 | + if (num_chgs > 0) { |
|---|
| 1317 | + int i; |
|---|
| 1318 | + |
|---|
| 1524 | 1319 | /* Allocate empty bin at the tail of array */ |
|---|
| 1525 | 1320 | desc->psy_charger_stat = devm_kcalloc(dev, |
|---|
| 1526 | 1321 | num_chgs + 1, |
|---|
| 1527 | 1322 | sizeof(char *), |
|---|
| 1528 | 1323 | GFP_KERNEL); |
|---|
| 1529 | | - if (desc->psy_charger_stat) { |
|---|
| 1530 | | - int i; |
|---|
| 1531 | | - for (i = 0; i < num_chgs; i++) |
|---|
| 1532 | | - of_property_read_string_index(np, "cm-chargers", |
|---|
| 1533 | | - i, &desc->psy_charger_stat[i]); |
|---|
| 1534 | | - } else { |
|---|
| 1324 | + if (!desc->psy_charger_stat) |
|---|
| 1535 | 1325 | return ERR_PTR(-ENOMEM); |
|---|
| 1536 | | - } |
|---|
| 1326 | + |
|---|
| 1327 | + for (i = 0; i < num_chgs; i++) |
|---|
| 1328 | + of_property_read_string_index(np, "cm-chargers", |
|---|
| 1329 | + i, &desc->psy_charger_stat[i]); |
|---|
| 1537 | 1330 | } |
|---|
| 1538 | 1331 | |
|---|
| 1539 | 1332 | of_property_read_string(np, "cm-fuel-gauge", &desc->psy_fuel_gauge); |
|---|
| .. | .. |
|---|
| 1551 | 1344 | of_property_read_u32(np, "cm-discharging-max", |
|---|
| 1552 | 1345 | &desc->discharging_max_duration_ms); |
|---|
| 1553 | 1346 | |
|---|
| 1554 | | - /* battery charger regualtors */ |
|---|
| 1347 | + /* battery charger regulators */ |
|---|
| 1555 | 1348 | desc->num_charger_regulators = of_get_child_count(np); |
|---|
| 1556 | 1349 | if (desc->num_charger_regulators) { |
|---|
| 1557 | 1350 | struct charger_regulator *chg_regs; |
|---|
| .. | .. |
|---|
| 1565 | 1358 | return ERR_PTR(-ENOMEM); |
|---|
| 1566 | 1359 | |
|---|
| 1567 | 1360 | desc->charger_regulators = chg_regs; |
|---|
| 1361 | + |
|---|
| 1362 | + desc->sysfs_groups = devm_kcalloc(dev, |
|---|
| 1363 | + desc->num_charger_regulators + 1, |
|---|
| 1364 | + sizeof(*desc->sysfs_groups), |
|---|
| 1365 | + GFP_KERNEL); |
|---|
| 1366 | + if (!desc->sysfs_groups) |
|---|
| 1367 | + return ERR_PTR(-ENOMEM); |
|---|
| 1568 | 1368 | |
|---|
| 1569 | 1369 | for_each_child_of_node(np, child) { |
|---|
| 1570 | 1370 | struct charger_cable *cables; |
|---|
| .. | .. |
|---|
| 1626 | 1426 | struct charger_desc *desc = cm_get_drv_data(pdev); |
|---|
| 1627 | 1427 | struct charger_manager *cm; |
|---|
| 1628 | 1428 | int ret, i = 0; |
|---|
| 1629 | | - int j = 0; |
|---|
| 1630 | 1429 | union power_supply_propval val; |
|---|
| 1631 | 1430 | struct power_supply *fuel_gauge; |
|---|
| 1431 | + enum power_supply_property *properties; |
|---|
| 1432 | + size_t num_properties; |
|---|
| 1632 | 1433 | struct power_supply_config psy_cfg = {}; |
|---|
| 1633 | 1434 | |
|---|
| 1634 | 1435 | if (IS_ERR(desc)) { |
|---|
| .. | .. |
|---|
| 1660 | 1461 | if (desc->fullbatt_uV == 0) { |
|---|
| 1661 | 1462 | dev_info(&pdev->dev, "Ignoring full-battery voltage threshold as it is not supplied\n"); |
|---|
| 1662 | 1463 | } |
|---|
| 1663 | | - if (!desc->fullbatt_vchkdrop_ms || !desc->fullbatt_vchkdrop_uV) { |
|---|
| 1464 | + if (!desc->fullbatt_vchkdrop_uV) { |
|---|
| 1664 | 1465 | dev_info(&pdev->dev, "Disabling full-battery voltage drop checking mechanism as it is not supplied\n"); |
|---|
| 1665 | | - desc->fullbatt_vchkdrop_ms = 0; |
|---|
| 1666 | 1466 | desc->fullbatt_vchkdrop_uV = 0; |
|---|
| 1667 | 1467 | } |
|---|
| 1668 | 1468 | if (desc->fullbatt_soc == 0) { |
|---|
| .. | .. |
|---|
| 1686 | 1486 | dev_err(&pdev->dev, "No fuel gauge power supply defined\n"); |
|---|
| 1687 | 1487 | return -EINVAL; |
|---|
| 1688 | 1488 | } |
|---|
| 1689 | | - |
|---|
| 1690 | | - /* Counting index only */ |
|---|
| 1691 | | - while (desc->psy_charger_stat[i]) |
|---|
| 1692 | | - i++; |
|---|
| 1693 | 1489 | |
|---|
| 1694 | 1490 | /* Check if charger's supplies are present at probe */ |
|---|
| 1695 | 1491 | for (i = 0; desc->psy_charger_stat[i]; i++) { |
|---|
| .. | .. |
|---|
| 1729 | 1525 | cm->charger_psy_desc.name = cm->psy_name_buf; |
|---|
| 1730 | 1526 | |
|---|
| 1731 | 1527 | /* Allocate for psy properties because they may vary */ |
|---|
| 1732 | | - cm->charger_psy_desc.properties = |
|---|
| 1733 | | - devm_kcalloc(&pdev->dev, |
|---|
| 1528 | + properties = devm_kcalloc(&pdev->dev, |
|---|
| 1734 | 1529 | ARRAY_SIZE(default_charger_props) + |
|---|
| 1735 | 1530 | NUM_CHARGER_PSY_OPTIONAL, |
|---|
| 1736 | | - sizeof(enum power_supply_property), GFP_KERNEL); |
|---|
| 1737 | | - if (!cm->charger_psy_desc.properties) |
|---|
| 1531 | + sizeof(*properties), GFP_KERNEL); |
|---|
| 1532 | + if (!properties) |
|---|
| 1738 | 1533 | return -ENOMEM; |
|---|
| 1739 | 1534 | |
|---|
| 1740 | | - memcpy(cm->charger_psy_desc.properties, default_charger_props, |
|---|
| 1535 | + memcpy(properties, default_charger_props, |
|---|
| 1741 | 1536 | sizeof(enum power_supply_property) * |
|---|
| 1742 | 1537 | ARRAY_SIZE(default_charger_props)); |
|---|
| 1743 | | - cm->charger_psy_desc.num_properties = psy_default.num_properties; |
|---|
| 1538 | + num_properties = ARRAY_SIZE(default_charger_props); |
|---|
| 1744 | 1539 | |
|---|
| 1745 | 1540 | /* Find which optional psy-properties are available */ |
|---|
| 1746 | 1541 | fuel_gauge = power_supply_get_by_name(desc->psy_fuel_gauge); |
|---|
| .. | .. |
|---|
| 1750 | 1545 | return -ENODEV; |
|---|
| 1751 | 1546 | } |
|---|
| 1752 | 1547 | if (!power_supply_get_property(fuel_gauge, |
|---|
| 1548 | + POWER_SUPPLY_PROP_CHARGE_FULL, &val)) { |
|---|
| 1549 | + properties[num_properties] = |
|---|
| 1550 | + POWER_SUPPLY_PROP_CHARGE_FULL; |
|---|
| 1551 | + num_properties++; |
|---|
| 1552 | + } |
|---|
| 1553 | + if (!power_supply_get_property(fuel_gauge, |
|---|
| 1753 | 1554 | POWER_SUPPLY_PROP_CHARGE_NOW, &val)) { |
|---|
| 1754 | | - cm->charger_psy_desc.properties[cm->charger_psy_desc.num_properties] = |
|---|
| 1555 | + properties[num_properties] = |
|---|
| 1755 | 1556 | POWER_SUPPLY_PROP_CHARGE_NOW; |
|---|
| 1756 | | - cm->charger_psy_desc.num_properties++; |
|---|
| 1557 | + num_properties++; |
|---|
| 1757 | 1558 | } |
|---|
| 1758 | 1559 | if (!power_supply_get_property(fuel_gauge, |
|---|
| 1759 | 1560 | POWER_SUPPLY_PROP_CURRENT_NOW, |
|---|
| 1760 | 1561 | &val)) { |
|---|
| 1761 | | - cm->charger_psy_desc.properties[cm->charger_psy_desc.num_properties] = |
|---|
| 1562 | + properties[num_properties] = |
|---|
| 1762 | 1563 | POWER_SUPPLY_PROP_CURRENT_NOW; |
|---|
| 1763 | | - cm->charger_psy_desc.num_properties++; |
|---|
| 1564 | + num_properties++; |
|---|
| 1764 | 1565 | } |
|---|
| 1765 | 1566 | |
|---|
| 1766 | | - ret = cm_init_thermal_data(cm, fuel_gauge); |
|---|
| 1567 | + ret = cm_init_thermal_data(cm, fuel_gauge, properties, &num_properties); |
|---|
| 1767 | 1568 | if (ret) { |
|---|
| 1768 | 1569 | dev_err(&pdev->dev, "Failed to initialize thermal data\n"); |
|---|
| 1769 | 1570 | cm->desc->measure_battery_temp = false; |
|---|
| 1770 | 1571 | } |
|---|
| 1771 | 1572 | power_supply_put(fuel_gauge); |
|---|
| 1772 | 1573 | |
|---|
| 1773 | | - INIT_DELAYED_WORK(&cm->fullbatt_vchk_work, fullbatt_vchk); |
|---|
| 1574 | + cm->charger_psy_desc.properties = properties; |
|---|
| 1575 | + cm->charger_psy_desc.num_properties = num_properties; |
|---|
| 1576 | + |
|---|
| 1577 | + /* Register sysfs entry for charger(regulator) */ |
|---|
| 1578 | + ret = charger_manager_prepare_sysfs(cm); |
|---|
| 1579 | + if (ret < 0) { |
|---|
| 1580 | + dev_err(&pdev->dev, |
|---|
| 1581 | + "Cannot prepare sysfs entry of regulators\n"); |
|---|
| 1582 | + return ret; |
|---|
| 1583 | + } |
|---|
| 1584 | + psy_cfg.attr_grp = desc->sysfs_groups; |
|---|
| 1774 | 1585 | |
|---|
| 1775 | 1586 | cm->charger_psy = power_supply_register(&pdev->dev, |
|---|
| 1776 | 1587 | &cm->charger_psy_desc, |
|---|
| .. | .. |
|---|
| 1788 | 1599 | goto err_reg_extcon; |
|---|
| 1789 | 1600 | } |
|---|
| 1790 | 1601 | |
|---|
| 1791 | | - /* Register sysfs entry for charger(regulator) */ |
|---|
| 1792 | | - ret = charger_manager_register_sysfs(cm); |
|---|
| 1793 | | - if (ret < 0) { |
|---|
| 1794 | | - dev_err(&pdev->dev, |
|---|
| 1795 | | - "Cannot initialize sysfs entry of regulator\n"); |
|---|
| 1796 | | - goto err_reg_sysfs; |
|---|
| 1797 | | - } |
|---|
| 1798 | | - |
|---|
| 1799 | 1602 | /* Add to the list */ |
|---|
| 1800 | 1603 | mutex_lock(&cm_list_mtx); |
|---|
| 1801 | 1604 | list_add(&cm->entry, &cm_list); |
|---|
| .. | .. |
|---|
| 1803 | 1606 | |
|---|
| 1804 | 1607 | /* |
|---|
| 1805 | 1608 | * Charger-manager is capable of waking up the systme from sleep |
|---|
| 1806 | | - * when event is happend through cm_notify_event() |
|---|
| 1609 | + * when event is happened through cm_notify_event() |
|---|
| 1807 | 1610 | */ |
|---|
| 1808 | 1611 | device_init_wakeup(&pdev->dev, true); |
|---|
| 1809 | 1612 | device_set_wakeup_capable(&pdev->dev, false); |
|---|
| 1810 | 1613 | |
|---|
| 1811 | 1614 | /* |
|---|
| 1812 | 1615 | * Charger-manager have to check the charging state right after |
|---|
| 1813 | | - * tialization of charger-manager and then update current charging |
|---|
| 1616 | + * initialization of charger-manager and then update current charging |
|---|
| 1814 | 1617 | * state. |
|---|
| 1815 | 1618 | */ |
|---|
| 1816 | 1619 | cm_monitor(); |
|---|
| .. | .. |
|---|
| 1819 | 1622 | |
|---|
| 1820 | 1623 | return 0; |
|---|
| 1821 | 1624 | |
|---|
| 1822 | | -err_reg_sysfs: |
|---|
| 1823 | | - for (i = 0; i < desc->num_charger_regulators; i++) { |
|---|
| 1824 | | - struct charger_regulator *charger; |
|---|
| 1825 | | - |
|---|
| 1826 | | - charger = &desc->charger_regulators[i]; |
|---|
| 1827 | | - sysfs_remove_group(&cm->charger_psy->dev.kobj, |
|---|
| 1828 | | - &charger->attr_g); |
|---|
| 1829 | | - } |
|---|
| 1830 | 1625 | err_reg_extcon: |
|---|
| 1831 | | - for (i = 0; i < desc->num_charger_regulators; i++) { |
|---|
| 1832 | | - struct charger_regulator *charger; |
|---|
| 1833 | | - |
|---|
| 1834 | | - charger = &desc->charger_regulators[i]; |
|---|
| 1835 | | - for (j = 0; j < charger->num_cables; j++) { |
|---|
| 1836 | | - struct charger_cable *cable = &charger->cables[j]; |
|---|
| 1837 | | - /* Remove notifier block if only edev exists */ |
|---|
| 1838 | | - if (cable->extcon_dev.edev) |
|---|
| 1839 | | - extcon_unregister_interest(&cable->extcon_dev); |
|---|
| 1840 | | - } |
|---|
| 1841 | | - |
|---|
| 1626 | + for (i = 0; i < desc->num_charger_regulators; i++) |
|---|
| 1842 | 1627 | regulator_put(desc->charger_regulators[i].consumer); |
|---|
| 1843 | | - } |
|---|
| 1844 | 1628 | |
|---|
| 1845 | 1629 | power_supply_unregister(cm->charger_psy); |
|---|
| 1846 | 1630 | |
|---|
| .. | .. |
|---|
| 1852 | 1636 | struct charger_manager *cm = platform_get_drvdata(pdev); |
|---|
| 1853 | 1637 | struct charger_desc *desc = cm->desc; |
|---|
| 1854 | 1638 | int i = 0; |
|---|
| 1855 | | - int j = 0; |
|---|
| 1856 | 1639 | |
|---|
| 1857 | 1640 | /* Remove from the list */ |
|---|
| 1858 | 1641 | mutex_lock(&cm_list_mtx); |
|---|
| .. | .. |
|---|
| 1861 | 1644 | |
|---|
| 1862 | 1645 | cancel_work_sync(&setup_polling); |
|---|
| 1863 | 1646 | cancel_delayed_work_sync(&cm_monitor_work); |
|---|
| 1864 | | - |
|---|
| 1865 | | - for (i = 0 ; i < desc->num_charger_regulators ; i++) { |
|---|
| 1866 | | - struct charger_regulator *charger |
|---|
| 1867 | | - = &desc->charger_regulators[i]; |
|---|
| 1868 | | - for (j = 0 ; j < charger->num_cables ; j++) { |
|---|
| 1869 | | - struct charger_cable *cable = &charger->cables[j]; |
|---|
| 1870 | | - extcon_unregister_interest(&cable->extcon_dev); |
|---|
| 1871 | | - } |
|---|
| 1872 | | - } |
|---|
| 1873 | 1647 | |
|---|
| 1874 | 1648 | for (i = 0 ; i < desc->num_charger_regulators ; i++) |
|---|
| 1875 | 1649 | regulator_put(desc->charger_regulators[i].consumer); |
|---|
| .. | .. |
|---|
| 1918 | 1692 | |
|---|
| 1919 | 1693 | static int cm_suspend_prepare(struct device *dev) |
|---|
| 1920 | 1694 | { |
|---|
| 1921 | | - struct charger_manager *cm = dev_get_drvdata(dev); |
|---|
| 1922 | | - |
|---|
| 1923 | 1695 | if (cm_need_to_awake()) |
|---|
| 1924 | 1696 | return -EBUSY; |
|---|
| 1925 | 1697 | |
|---|
| .. | .. |
|---|
| 1931 | 1703 | if (cm_timer_set) { |
|---|
| 1932 | 1704 | cancel_work_sync(&setup_polling); |
|---|
| 1933 | 1705 | cancel_delayed_work_sync(&cm_monitor_work); |
|---|
| 1934 | | - cancel_delayed_work(&cm->fullbatt_vchk_work); |
|---|
| 1935 | 1706 | } |
|---|
| 1936 | 1707 | |
|---|
| 1937 | 1708 | return 0; |
|---|
| .. | .. |
|---|
| 1956 | 1727 | |
|---|
| 1957 | 1728 | _cm_monitor(cm); |
|---|
| 1958 | 1729 | |
|---|
| 1959 | | - /* Re-enqueue delayed work (fullbatt_vchk_work) */ |
|---|
| 1960 | | - if (cm->fullbatt_vchk_jiffies_at) { |
|---|
| 1961 | | - unsigned long delay = 0; |
|---|
| 1962 | | - unsigned long now = jiffies + CM_JIFFIES_SMALL; |
|---|
| 1963 | | - |
|---|
| 1964 | | - if (time_after_eq(now, cm->fullbatt_vchk_jiffies_at)) { |
|---|
| 1965 | | - delay = (unsigned long)((long)now |
|---|
| 1966 | | - - (long)(cm->fullbatt_vchk_jiffies_at)); |
|---|
| 1967 | | - delay = jiffies_to_msecs(delay); |
|---|
| 1968 | | - } else { |
|---|
| 1969 | | - delay = 0; |
|---|
| 1970 | | - } |
|---|
| 1971 | | - |
|---|
| 1972 | | - /* |
|---|
| 1973 | | - * Account for cm_suspend_duration_ms with assuming that |
|---|
| 1974 | | - * timer stops in suspend. |
|---|
| 1975 | | - */ |
|---|
| 1976 | | - if (delay > cm_suspend_duration_ms) |
|---|
| 1977 | | - delay -= cm_suspend_duration_ms; |
|---|
| 1978 | | - else |
|---|
| 1979 | | - delay = 0; |
|---|
| 1980 | | - |
|---|
| 1981 | | - queue_delayed_work(cm_wq, &cm->fullbatt_vchk_work, |
|---|
| 1982 | | - msecs_to_jiffies(delay)); |
|---|
| 1983 | | - } |
|---|
| 1984 | 1730 | device_set_wakeup_capable(cm->dev, false); |
|---|
| 1985 | 1731 | } |
|---|
| 1986 | 1732 | |
|---|
| .. | .. |
|---|
| 2004 | 1750 | static int __init charger_manager_init(void) |
|---|
| 2005 | 1751 | { |
|---|
| 2006 | 1752 | cm_wq = create_freezable_workqueue("charger_manager"); |
|---|
| 1753 | + if (unlikely(!cm_wq)) |
|---|
| 1754 | + return -ENOMEM; |
|---|
| 1755 | + |
|---|
| 2007 | 1756 | INIT_DELAYED_WORK(&cm_monitor_work, cm_monitor_poller); |
|---|
| 2008 | 1757 | |
|---|
| 2009 | 1758 | return platform_driver_register(&charger_manager_driver); |
|---|
| .. | .. |
|---|
| 2018 | 1767 | platform_driver_unregister(&charger_manager_driver); |
|---|
| 2019 | 1768 | } |
|---|
| 2020 | 1769 | module_exit(charger_manager_cleanup); |
|---|
| 2021 | | - |
|---|
| 2022 | | -/** |
|---|
| 2023 | | - * cm_notify_event - charger driver notify Charger Manager of charger event |
|---|
| 2024 | | - * @psy: pointer to instance of charger's power_supply |
|---|
| 2025 | | - * @type: type of charger event |
|---|
| 2026 | | - * @msg: optional message passed to uevent_notify fuction |
|---|
| 2027 | | - */ |
|---|
| 2028 | | -void cm_notify_event(struct power_supply *psy, enum cm_event_types type, |
|---|
| 2029 | | - char *msg) |
|---|
| 2030 | | -{ |
|---|
| 2031 | | - struct charger_manager *cm; |
|---|
| 2032 | | - bool found_power_supply = false; |
|---|
| 2033 | | - |
|---|
| 2034 | | - if (psy == NULL) |
|---|
| 2035 | | - return; |
|---|
| 2036 | | - |
|---|
| 2037 | | - mutex_lock(&cm_list_mtx); |
|---|
| 2038 | | - list_for_each_entry(cm, &cm_list, entry) { |
|---|
| 2039 | | - if (match_string(cm->desc->psy_charger_stat, -1, |
|---|
| 2040 | | - psy->desc->name) >= 0) { |
|---|
| 2041 | | - found_power_supply = true; |
|---|
| 2042 | | - break; |
|---|
| 2043 | | - } |
|---|
| 2044 | | - } |
|---|
| 2045 | | - mutex_unlock(&cm_list_mtx); |
|---|
| 2046 | | - |
|---|
| 2047 | | - if (!found_power_supply) |
|---|
| 2048 | | - return; |
|---|
| 2049 | | - |
|---|
| 2050 | | - switch (type) { |
|---|
| 2051 | | - case CM_EVENT_BATT_FULL: |
|---|
| 2052 | | - fullbatt_handler(cm); |
|---|
| 2053 | | - break; |
|---|
| 2054 | | - case CM_EVENT_BATT_OUT: |
|---|
| 2055 | | - battout_handler(cm); |
|---|
| 2056 | | - break; |
|---|
| 2057 | | - case CM_EVENT_BATT_IN: |
|---|
| 2058 | | - case CM_EVENT_EXT_PWR_IN_OUT ... CM_EVENT_CHG_START_STOP: |
|---|
| 2059 | | - misc_event_handler(cm, type); |
|---|
| 2060 | | - break; |
|---|
| 2061 | | - case CM_EVENT_UNKNOWN: |
|---|
| 2062 | | - case CM_EVENT_OTHERS: |
|---|
| 2063 | | - uevent_notify(cm, msg ? msg : default_event_names[type]); |
|---|
| 2064 | | - break; |
|---|
| 2065 | | - default: |
|---|
| 2066 | | - dev_err(cm->dev, "%s: type not specified\n", __func__); |
|---|
| 2067 | | - break; |
|---|
| 2068 | | - } |
|---|
| 2069 | | -} |
|---|
| 2070 | | -EXPORT_SYMBOL_GPL(cm_notify_event); |
|---|
| 2071 | 1770 | |
|---|
| 2072 | 1771 | MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>"); |
|---|
| 2073 | 1772 | MODULE_DESCRIPTION("Charger Manager"); |
|---|