1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 23bb3dbbdSDonggeun Kim /* 33bb3dbbdSDonggeun Kim * Copyright (C) 2011 Samsung Electronics Co., Ltd. 43bb3dbbdSDonggeun Kim * MyungJoo Ham <myungjoo.ham@samsung.com> 53bb3dbbdSDonggeun Kim * 63bb3dbbdSDonggeun Kim * This driver enables to monitor battery health and control charger 73bb3dbbdSDonggeun Kim * during suspend-to-mem. 802276af2SKrzysztof Kozlowski * Charger manager depends on other devices. Register this later than 93bb3dbbdSDonggeun Kim * the depending devices. 103bb3dbbdSDonggeun Kim * 113bb3dbbdSDonggeun Kim **/ 123bb3dbbdSDonggeun Kim 13e5409cbdSJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 14e5409cbdSJoe Perches 153bb3dbbdSDonggeun Kim #include <linux/io.h> 163bb3dbbdSDonggeun Kim #include <linux/module.h> 173bb3dbbdSDonggeun Kim #include <linux/irq.h> 183bb3dbbdSDonggeun Kim #include <linux/interrupt.h> 193bb3dbbdSDonggeun Kim #include <linux/rtc.h> 203bb3dbbdSDonggeun Kim #include <linux/slab.h> 213bb3dbbdSDonggeun Kim #include <linux/workqueue.h> 223bb3dbbdSDonggeun Kim #include <linux/platform_device.h> 233bb3dbbdSDonggeun Kim #include <linux/power/charger-manager.h> 243bb3dbbdSDonggeun Kim #include <linux/regulator/consumer.h> 253950c786SChanwoo Choi #include <linux/sysfs.h> 26856ee611SJonghwa Lee #include <linux/of.h> 275c49a625SJonghwa Lee #include <linux/thermal.h> 285c49a625SJonghwa Lee 295c49a625SJonghwa Lee /* 3002276af2SKrzysztof Kozlowski * Default temperature threshold for charging. 315c49a625SJonghwa Lee * Every temperature units are in tenth of centigrade. 325c49a625SJonghwa Lee */ 335c49a625SJonghwa Lee #define CM_DEFAULT_RECHARGE_TEMP_DIFF 50 345c49a625SJonghwa Lee #define CM_DEFAULT_CHARGE_TEMP_MAX 500 353bb3dbbdSDonggeun Kim 363bb3dbbdSDonggeun Kim /* 373bb3dbbdSDonggeun Kim * Regard CM_JIFFIES_SMALL jiffies is small enough to ignore for 383bb3dbbdSDonggeun Kim * delayed works so that we can run delayed works with CM_JIFFIES_SMALL 393bb3dbbdSDonggeun Kim * without any delays. 403bb3dbbdSDonggeun Kim */ 413bb3dbbdSDonggeun Kim #define CM_JIFFIES_SMALL (2) 423bb3dbbdSDonggeun Kim 433bb3dbbdSDonggeun Kim /* If y is valid (> 0) and smaller than x, do x = y */ 443bb3dbbdSDonggeun Kim #define CM_MIN_VALID(x, y) x = (((y > 0) && ((x) > (y))) ? (y) : (x)) 453bb3dbbdSDonggeun Kim 463bb3dbbdSDonggeun Kim /* 473bb3dbbdSDonggeun Kim * Regard CM_RTC_SMALL (sec) is small enough to ignore error in invoking 483bb3dbbdSDonggeun Kim * rtc alarm. It should be 2 or larger 493bb3dbbdSDonggeun Kim */ 503bb3dbbdSDonggeun Kim #define CM_RTC_SMALL (2) 513bb3dbbdSDonggeun Kim 523bb3dbbdSDonggeun Kim static LIST_HEAD(cm_list); 533bb3dbbdSDonggeun Kim static DEFINE_MUTEX(cm_list_mtx); 543bb3dbbdSDonggeun Kim 553bb3dbbdSDonggeun Kim /* About in-suspend (suspend-again) monitoring */ 56c1155c64SJonghwa Lee static struct alarm *cm_timer; 57c1155c64SJonghwa Lee 583bb3dbbdSDonggeun Kim static bool cm_suspended; 59c1155c64SJonghwa Lee static bool cm_timer_set; 603bb3dbbdSDonggeun Kim static unsigned long cm_suspend_duration_ms; 613bb3dbbdSDonggeun Kim 62d829dc75SChanwoo Choi /* About normal (not suspended) monitoring */ 63d829dc75SChanwoo Choi static unsigned long polling_jiffy = ULONG_MAX; /* ULONG_MAX: no polling */ 64d829dc75SChanwoo Choi static unsigned long next_polling; /* Next appointed polling time */ 65d829dc75SChanwoo Choi static struct workqueue_struct *cm_wq; /* init at driver add */ 66d829dc75SChanwoo Choi static struct delayed_work cm_monitor_work; /* init at driver add */ 67d829dc75SChanwoo Choi 683bb3dbbdSDonggeun Kim /** 693bb3dbbdSDonggeun Kim * is_batt_present - See if the battery presents in place. 703bb3dbbdSDonggeun Kim * @cm: the Charger Manager representing the battery. 713bb3dbbdSDonggeun Kim */ 723bb3dbbdSDonggeun Kim static bool is_batt_present(struct charger_manager *cm) 733bb3dbbdSDonggeun Kim { 743bb3dbbdSDonggeun Kim union power_supply_propval val; 75bdbe8144SKrzysztof Kozlowski struct power_supply *psy; 763bb3dbbdSDonggeun Kim bool present = false; 773bb3dbbdSDonggeun Kim int i, ret; 783bb3dbbdSDonggeun Kim 793bb3dbbdSDonggeun Kim switch (cm->desc->battery_present) { 80d829dc75SChanwoo Choi case CM_BATTERY_PRESENT: 81d829dc75SChanwoo Choi present = true; 82d829dc75SChanwoo Choi break; 83d829dc75SChanwoo Choi case CM_NO_BATTERY: 84d829dc75SChanwoo Choi break; 853bb3dbbdSDonggeun Kim case CM_FUEL_GAUGE: 86bdbe8144SKrzysztof Kozlowski psy = power_supply_get_by_name(cm->desc->psy_fuel_gauge); 87bdbe8144SKrzysztof Kozlowski if (!psy) 88bdbe8144SKrzysztof Kozlowski break; 89bdbe8144SKrzysztof Kozlowski 90b70229bcSKrzysztof Kozlowski ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_PRESENT, 91b70229bcSKrzysztof Kozlowski &val); 923bb3dbbdSDonggeun Kim if (ret == 0 && val.intval) 933bb3dbbdSDonggeun Kim present = true; 94b43eb35aSKrzysztof Kozlowski power_supply_put(psy); 953bb3dbbdSDonggeun Kim break; 963bb3dbbdSDonggeun Kim case CM_CHARGER_STAT: 97cdaf3e15SKrzysztof Kozlowski for (i = 0; cm->desc->psy_charger_stat[i]; i++) { 98cdaf3e15SKrzysztof Kozlowski psy = power_supply_get_by_name( 99cdaf3e15SKrzysztof Kozlowski cm->desc->psy_charger_stat[i]); 100cdaf3e15SKrzysztof Kozlowski if (!psy) { 101cdaf3e15SKrzysztof Kozlowski dev_err(cm->dev, "Cannot find power supply \"%s\"\n", 102cdaf3e15SKrzysztof Kozlowski cm->desc->psy_charger_stat[i]); 103cdaf3e15SKrzysztof Kozlowski continue; 104cdaf3e15SKrzysztof Kozlowski } 105cdaf3e15SKrzysztof Kozlowski 106b70229bcSKrzysztof Kozlowski ret = power_supply_get_property(psy, 107b70229bcSKrzysztof Kozlowski POWER_SUPPLY_PROP_PRESENT, &val); 108b43eb35aSKrzysztof Kozlowski power_supply_put(psy); 1093bb3dbbdSDonggeun Kim if (ret == 0 && val.intval) { 1103bb3dbbdSDonggeun Kim present = true; 1113bb3dbbdSDonggeun Kim break; 1123bb3dbbdSDonggeun Kim } 1133bb3dbbdSDonggeun Kim } 1143bb3dbbdSDonggeun Kim break; 1153bb3dbbdSDonggeun Kim } 1163bb3dbbdSDonggeun Kim 1173bb3dbbdSDonggeun Kim return present; 1183bb3dbbdSDonggeun Kim } 1193bb3dbbdSDonggeun Kim 1203bb3dbbdSDonggeun Kim /** 1213bb3dbbdSDonggeun Kim * is_ext_pwr_online - See if an external power source is attached to charge 1223bb3dbbdSDonggeun Kim * @cm: the Charger Manager representing the battery. 1233bb3dbbdSDonggeun Kim * 1243bb3dbbdSDonggeun Kim * Returns true if at least one of the chargers of the battery has an external 1253bb3dbbdSDonggeun Kim * power source attached to charge the battery regardless of whether it is 1263bb3dbbdSDonggeun Kim * actually charging or not. 1273bb3dbbdSDonggeun Kim */ 1283bb3dbbdSDonggeun Kim static bool is_ext_pwr_online(struct charger_manager *cm) 1293bb3dbbdSDonggeun Kim { 1303bb3dbbdSDonggeun Kim union power_supply_propval val; 131cdaf3e15SKrzysztof Kozlowski struct power_supply *psy; 1323bb3dbbdSDonggeun Kim bool online = false; 1333bb3dbbdSDonggeun Kim int i, ret; 1343bb3dbbdSDonggeun Kim 1353bb3dbbdSDonggeun Kim /* If at least one of them has one, it's yes. */ 136cdaf3e15SKrzysztof Kozlowski for (i = 0; cm->desc->psy_charger_stat[i]; i++) { 137cdaf3e15SKrzysztof Kozlowski psy = power_supply_get_by_name(cm->desc->psy_charger_stat[i]); 138cdaf3e15SKrzysztof Kozlowski if (!psy) { 139cdaf3e15SKrzysztof Kozlowski dev_err(cm->dev, "Cannot find power supply \"%s\"\n", 140cdaf3e15SKrzysztof Kozlowski cm->desc->psy_charger_stat[i]); 141cdaf3e15SKrzysztof Kozlowski continue; 142cdaf3e15SKrzysztof Kozlowski } 143cdaf3e15SKrzysztof Kozlowski 144b70229bcSKrzysztof Kozlowski ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_ONLINE, 145b70229bcSKrzysztof Kozlowski &val); 146b43eb35aSKrzysztof Kozlowski power_supply_put(psy); 1473bb3dbbdSDonggeun Kim if (ret == 0 && val.intval) { 1483bb3dbbdSDonggeun Kim online = true; 1493bb3dbbdSDonggeun Kim break; 1503bb3dbbdSDonggeun Kim } 1513bb3dbbdSDonggeun Kim } 1523bb3dbbdSDonggeun Kim 1533bb3dbbdSDonggeun Kim return online; 1543bb3dbbdSDonggeun Kim } 1553bb3dbbdSDonggeun Kim 1563bb3dbbdSDonggeun Kim /** 157ad3d13eeSDonggeun Kim * get_batt_uV - Get the voltage level of the battery 158ad3d13eeSDonggeun Kim * @cm: the Charger Manager representing the battery. 159ad3d13eeSDonggeun Kim * @uV: the voltage level returned. 160ad3d13eeSDonggeun Kim * 161ad3d13eeSDonggeun Kim * Returns 0 if there is no error. 162ad3d13eeSDonggeun Kim * Returns a negative value on error. 163ad3d13eeSDonggeun Kim */ 164ad3d13eeSDonggeun Kim static int get_batt_uV(struct charger_manager *cm, int *uV) 165ad3d13eeSDonggeun Kim { 166ad3d13eeSDonggeun Kim union power_supply_propval val; 167bdbe8144SKrzysztof Kozlowski struct power_supply *fuel_gauge; 168ad3d13eeSDonggeun Kim int ret; 169ad3d13eeSDonggeun Kim 170bdbe8144SKrzysztof Kozlowski fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge); 171bdbe8144SKrzysztof Kozlowski if (!fuel_gauge) 172ad3d13eeSDonggeun Kim return -ENODEV; 173ad3d13eeSDonggeun Kim 174b70229bcSKrzysztof Kozlowski ret = power_supply_get_property(fuel_gauge, 175bb2a95c2SAxel Lin POWER_SUPPLY_PROP_VOLTAGE_NOW, &val); 176b43eb35aSKrzysztof Kozlowski power_supply_put(fuel_gauge); 177ad3d13eeSDonggeun Kim if (ret) 178ad3d13eeSDonggeun Kim return ret; 179ad3d13eeSDonggeun Kim 180ad3d13eeSDonggeun Kim *uV = val.intval; 181ad3d13eeSDonggeun Kim return 0; 182ad3d13eeSDonggeun Kim } 183ad3d13eeSDonggeun Kim 184ad3d13eeSDonggeun Kim /** 1853bb3dbbdSDonggeun Kim * is_charging - Returns true if the battery is being charged. 1863bb3dbbdSDonggeun Kim * @cm: the Charger Manager representing the battery. 1873bb3dbbdSDonggeun Kim */ 1883bb3dbbdSDonggeun Kim static bool is_charging(struct charger_manager *cm) 1893bb3dbbdSDonggeun Kim { 1903bb3dbbdSDonggeun Kim int i, ret; 1913bb3dbbdSDonggeun Kim bool charging = false; 192cdaf3e15SKrzysztof Kozlowski struct power_supply *psy; 1933bb3dbbdSDonggeun Kim union power_supply_propval val; 1943bb3dbbdSDonggeun Kim 1953bb3dbbdSDonggeun Kim /* If there is no battery, it cannot be charged */ 1963bb3dbbdSDonggeun Kim if (!is_batt_present(cm)) 1973bb3dbbdSDonggeun Kim return false; 1983bb3dbbdSDonggeun Kim 1993bb3dbbdSDonggeun Kim /* If at least one of the charger is charging, return yes */ 200cdaf3e15SKrzysztof Kozlowski for (i = 0; cm->desc->psy_charger_stat[i]; i++) { 2013bb3dbbdSDonggeun Kim /* 1. The charger sholuld not be DISABLED */ 2023bb3dbbdSDonggeun Kim if (cm->emergency_stop) 2033bb3dbbdSDonggeun Kim continue; 2043bb3dbbdSDonggeun Kim if (!cm->charger_enabled) 2053bb3dbbdSDonggeun Kim continue; 2063bb3dbbdSDonggeun Kim 207cdaf3e15SKrzysztof Kozlowski psy = power_supply_get_by_name(cm->desc->psy_charger_stat[i]); 208cdaf3e15SKrzysztof Kozlowski if (!psy) { 209cdaf3e15SKrzysztof Kozlowski dev_err(cm->dev, "Cannot find power supply \"%s\"\n", 210cdaf3e15SKrzysztof Kozlowski cm->desc->psy_charger_stat[i]); 211cdaf3e15SKrzysztof Kozlowski continue; 212cdaf3e15SKrzysztof Kozlowski } 213cdaf3e15SKrzysztof Kozlowski 2143bb3dbbdSDonggeun Kim /* 2. The charger should be online (ext-power) */ 215b70229bcSKrzysztof Kozlowski ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_ONLINE, 216b70229bcSKrzysztof Kozlowski &val); 2173bb3dbbdSDonggeun Kim if (ret) { 218e5409cbdSJoe Perches dev_warn(cm->dev, "Cannot read ONLINE value from %s\n", 2193bb3dbbdSDonggeun Kim cm->desc->psy_charger_stat[i]); 220b43eb35aSKrzysztof Kozlowski power_supply_put(psy); 2213bb3dbbdSDonggeun Kim continue; 2223bb3dbbdSDonggeun Kim } 223b43eb35aSKrzysztof Kozlowski if (val.intval == 0) { 224b43eb35aSKrzysztof Kozlowski power_supply_put(psy); 2253bb3dbbdSDonggeun Kim continue; 226b43eb35aSKrzysztof Kozlowski } 2273bb3dbbdSDonggeun Kim 2283bb3dbbdSDonggeun Kim /* 2293bb3dbbdSDonggeun Kim * 3. The charger should not be FULL, DISCHARGING, 2303bb3dbbdSDonggeun Kim * or NOT_CHARGING. 2313bb3dbbdSDonggeun Kim */ 232b70229bcSKrzysztof Kozlowski ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_STATUS, 233b70229bcSKrzysztof Kozlowski &val); 234b43eb35aSKrzysztof Kozlowski power_supply_put(psy); 2353bb3dbbdSDonggeun Kim if (ret) { 236e5409cbdSJoe Perches dev_warn(cm->dev, "Cannot read STATUS value from %s\n", 2373bb3dbbdSDonggeun Kim cm->desc->psy_charger_stat[i]); 2383bb3dbbdSDonggeun Kim continue; 2393bb3dbbdSDonggeun Kim } 2403bb3dbbdSDonggeun Kim if (val.intval == POWER_SUPPLY_STATUS_FULL || 2413bb3dbbdSDonggeun Kim val.intval == POWER_SUPPLY_STATUS_DISCHARGING || 2423bb3dbbdSDonggeun Kim val.intval == POWER_SUPPLY_STATUS_NOT_CHARGING) 2433bb3dbbdSDonggeun Kim continue; 2443bb3dbbdSDonggeun Kim 2453bb3dbbdSDonggeun Kim /* Then, this is charging. */ 2463bb3dbbdSDonggeun Kim charging = true; 2473bb3dbbdSDonggeun Kim break; 2483bb3dbbdSDonggeun Kim } 2493bb3dbbdSDonggeun Kim 2503bb3dbbdSDonggeun Kim return charging; 2513bb3dbbdSDonggeun Kim } 2523bb3dbbdSDonggeun Kim 2533bb3dbbdSDonggeun Kim /** 2542ed9e9b6SChanwoo Choi * is_full_charged - Returns true if the battery is fully charged. 2552ed9e9b6SChanwoo Choi * @cm: the Charger Manager representing the battery. 2562ed9e9b6SChanwoo Choi */ 2572ed9e9b6SChanwoo Choi static bool is_full_charged(struct charger_manager *cm) 2582ed9e9b6SChanwoo Choi { 2592ed9e9b6SChanwoo Choi struct charger_desc *desc = cm->desc; 2602ed9e9b6SChanwoo Choi union power_supply_propval val; 261bdbe8144SKrzysztof Kozlowski struct power_supply *fuel_gauge; 262b43eb35aSKrzysztof Kozlowski bool is_full = false; 2632ed9e9b6SChanwoo Choi int ret = 0; 2642ed9e9b6SChanwoo Choi int uV; 2652ed9e9b6SChanwoo Choi 2662ed9e9b6SChanwoo Choi /* If there is no battery, it cannot be charged */ 2670fa11dbcSChanwoo Choi if (!is_batt_present(cm)) 2680fa11dbcSChanwoo Choi return false; 2692ed9e9b6SChanwoo Choi 270bdbe8144SKrzysztof Kozlowski fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge); 271bdbe8144SKrzysztof Kozlowski if (!fuel_gauge) 272bdbe8144SKrzysztof Kozlowski return false; 273bdbe8144SKrzysztof Kozlowski 274e132fc6bSJonghwa Lee /* Full, if it's over the fullbatt voltage */ 275e132fc6bSJonghwa Lee if (desc->fullbatt_uV > 0) { 276e132fc6bSJonghwa Lee ret = get_batt_uV(cm, &uV); 277e132fc6bSJonghwa Lee if (!ret) { 278e132fc6bSJonghwa Lee /* Battery is already full, checks voltage drop. */ 279e132fc6bSJonghwa Lee if (cm->battery_status == POWER_SUPPLY_STATUS_FULL 280e132fc6bSJonghwa Lee && desc->fullbatt_vchkdrop_uV) 281e132fc6bSJonghwa Lee uV += desc->fullbatt_vchkdrop_uV; 282e132fc6bSJonghwa Lee if (uV >= desc->fullbatt_uV) 283e132fc6bSJonghwa Lee return true; 284e132fc6bSJonghwa Lee } 285e132fc6bSJonghwa Lee } 286e132fc6bSJonghwa Lee 287bdbe8144SKrzysztof Kozlowski if (desc->fullbatt_full_capacity > 0) { 2880fa11dbcSChanwoo Choi val.intval = 0; 2890fa11dbcSChanwoo Choi 2902ed9e9b6SChanwoo Choi /* Not full if capacity of fuel gauge isn't full */ 291b70229bcSKrzysztof Kozlowski ret = power_supply_get_property(fuel_gauge, 2922ed9e9b6SChanwoo Choi POWER_SUPPLY_PROP_CHARGE_FULL, &val); 293b43eb35aSKrzysztof Kozlowski if (!ret && val.intval > desc->fullbatt_full_capacity) { 294b43eb35aSKrzysztof Kozlowski is_full = true; 295b43eb35aSKrzysztof Kozlowski goto out; 296b43eb35aSKrzysztof Kozlowski } 2972ed9e9b6SChanwoo Choi } 2982ed9e9b6SChanwoo Choi 2992ed9e9b6SChanwoo Choi /* Full, if the capacity is more than fullbatt_soc */ 300bdbe8144SKrzysztof Kozlowski if (desc->fullbatt_soc > 0) { 3012ed9e9b6SChanwoo Choi val.intval = 0; 3022ed9e9b6SChanwoo Choi 303b70229bcSKrzysztof Kozlowski ret = power_supply_get_property(fuel_gauge, 3040fa11dbcSChanwoo Choi POWER_SUPPLY_PROP_CAPACITY, &val); 305b43eb35aSKrzysztof Kozlowski if (!ret && val.intval >= desc->fullbatt_soc) { 306b43eb35aSKrzysztof Kozlowski is_full = true; 307b43eb35aSKrzysztof Kozlowski goto out; 308b43eb35aSKrzysztof Kozlowski } 3090fa11dbcSChanwoo Choi } 3100fa11dbcSChanwoo Choi 311b43eb35aSKrzysztof Kozlowski out: 312b43eb35aSKrzysztof Kozlowski power_supply_put(fuel_gauge); 313b43eb35aSKrzysztof Kozlowski return is_full; 3142ed9e9b6SChanwoo Choi } 3152ed9e9b6SChanwoo Choi 3162ed9e9b6SChanwoo Choi /** 3173bb3dbbdSDonggeun Kim * is_polling_required - Return true if need to continue polling for this CM. 3183bb3dbbdSDonggeun Kim * @cm: the Charger Manager representing the battery. 3193bb3dbbdSDonggeun Kim */ 3203bb3dbbdSDonggeun Kim static bool is_polling_required(struct charger_manager *cm) 3213bb3dbbdSDonggeun Kim { 3223bb3dbbdSDonggeun Kim switch (cm->desc->polling_mode) { 3233bb3dbbdSDonggeun Kim case CM_POLL_DISABLE: 3243bb3dbbdSDonggeun Kim return false; 3253bb3dbbdSDonggeun Kim case CM_POLL_ALWAYS: 3263bb3dbbdSDonggeun Kim return true; 3273bb3dbbdSDonggeun Kim case CM_POLL_EXTERNAL_POWER_ONLY: 3283bb3dbbdSDonggeun Kim return is_ext_pwr_online(cm); 3293bb3dbbdSDonggeun Kim case CM_POLL_CHARGING_ONLY: 3303bb3dbbdSDonggeun Kim return is_charging(cm); 3313bb3dbbdSDonggeun Kim default: 3323bb3dbbdSDonggeun Kim dev_warn(cm->dev, "Incorrect polling_mode (%d)\n", 3333bb3dbbdSDonggeun Kim cm->desc->polling_mode); 3343bb3dbbdSDonggeun Kim } 3353bb3dbbdSDonggeun Kim 3363bb3dbbdSDonggeun Kim return false; 3373bb3dbbdSDonggeun Kim } 3383bb3dbbdSDonggeun Kim 3393bb3dbbdSDonggeun Kim /** 3403bb3dbbdSDonggeun Kim * try_charger_enable - Enable/Disable chargers altogether 3413bb3dbbdSDonggeun Kim * @cm: the Charger Manager representing the battery. 3423bb3dbbdSDonggeun Kim * @enable: true: enable / false: disable 3433bb3dbbdSDonggeun Kim * 3443bb3dbbdSDonggeun Kim * Note that Charger Manager keeps the charger enabled regardless whether 3453bb3dbbdSDonggeun Kim * the charger is charging or not (because battery is full or no external 3463bb3dbbdSDonggeun Kim * power source exists) except when CM needs to disable chargers forcibly 34702276af2SKrzysztof Kozlowski * because of emergency causes; when the battery is overheated or too cold. 3483bb3dbbdSDonggeun Kim */ 3493bb3dbbdSDonggeun Kim static int try_charger_enable(struct charger_manager *cm, bool enable) 3503bb3dbbdSDonggeun Kim { 3513bb3dbbdSDonggeun Kim int err = 0, i; 3523bb3dbbdSDonggeun Kim struct charger_desc *desc = cm->desc; 3533bb3dbbdSDonggeun Kim 3548c13b6f1SBaolin Wang /* Ignore if it's redundant command */ 355bb2a95c2SAxel Lin if (enable == cm->charger_enabled) 3563bb3dbbdSDonggeun Kim return 0; 3573bb3dbbdSDonggeun Kim 3583bb3dbbdSDonggeun Kim if (enable) { 3593bb3dbbdSDonggeun Kim if (cm->emergency_stop) 3603bb3dbbdSDonggeun Kim return -EAGAIN; 3618fcfe088SChanwoo Choi 3628fcfe088SChanwoo Choi /* 3638fcfe088SChanwoo Choi * Save start time of charging to limit 3648fcfe088SChanwoo Choi * maximum possible charging time. 3658fcfe088SChanwoo Choi */ 3668fcfe088SChanwoo Choi cm->charging_start_time = ktime_to_ms(ktime_get()); 3678fcfe088SChanwoo Choi cm->charging_end_time = 0; 3688fcfe088SChanwoo Choi 369dbb61fc7SChanwoo Choi for (i = 0 ; i < desc->num_charger_regulators ; i++) { 3703950c786SChanwoo Choi if (desc->charger_regulators[i].externally_control) 3713950c786SChanwoo Choi continue; 3723950c786SChanwoo Choi 373dbb61fc7SChanwoo Choi err = regulator_enable(desc->charger_regulators[i].consumer); 374dbb61fc7SChanwoo Choi if (err < 0) { 375e5409cbdSJoe Perches dev_warn(cm->dev, "Cannot enable %s regulator\n", 376dbb61fc7SChanwoo Choi desc->charger_regulators[i].regulator_name); 377dbb61fc7SChanwoo Choi } 378dbb61fc7SChanwoo Choi } 3793bb3dbbdSDonggeun Kim } else { 3803bb3dbbdSDonggeun Kim /* 3818fcfe088SChanwoo Choi * Save end time of charging to maintain fully charged state 3828fcfe088SChanwoo Choi * of battery after full-batt. 3838fcfe088SChanwoo Choi */ 3848fcfe088SChanwoo Choi cm->charging_start_time = 0; 3858fcfe088SChanwoo Choi cm->charging_end_time = ktime_to_ms(ktime_get()); 3868fcfe088SChanwoo Choi 387dbb61fc7SChanwoo Choi for (i = 0 ; i < desc->num_charger_regulators ; i++) { 3883950c786SChanwoo Choi if (desc->charger_regulators[i].externally_control) 3893950c786SChanwoo Choi continue; 3903950c786SChanwoo Choi 391dbb61fc7SChanwoo Choi err = regulator_disable(desc->charger_regulators[i].consumer); 392dbb61fc7SChanwoo Choi if (err < 0) { 393e5409cbdSJoe Perches dev_warn(cm->dev, "Cannot disable %s regulator\n", 394dbb61fc7SChanwoo Choi desc->charger_regulators[i].regulator_name); 395dbb61fc7SChanwoo Choi } 396dbb61fc7SChanwoo Choi } 397dbb61fc7SChanwoo Choi 3983bb3dbbdSDonggeun Kim /* 3993bb3dbbdSDonggeun Kim * Abnormal battery state - Stop charging forcibly, 4003bb3dbbdSDonggeun Kim * even if charger was enabled at the other places 4013bb3dbbdSDonggeun Kim */ 4023bb3dbbdSDonggeun Kim for (i = 0; i < desc->num_charger_regulators; i++) { 4033bb3dbbdSDonggeun Kim if (regulator_is_enabled( 4043bb3dbbdSDonggeun Kim desc->charger_regulators[i].consumer)) { 4053bb3dbbdSDonggeun Kim regulator_force_disable( 4063bb3dbbdSDonggeun Kim desc->charger_regulators[i].consumer); 407e5409cbdSJoe Perches dev_warn(cm->dev, "Disable regulator(%s) forcibly\n", 408bee737bcSChanwoo Choi desc->charger_regulators[i].regulator_name); 4093bb3dbbdSDonggeun Kim } 4103bb3dbbdSDonggeun Kim } 4113bb3dbbdSDonggeun Kim } 4123bb3dbbdSDonggeun Kim 413e132fc6bSJonghwa Lee if (!err) 4143bb3dbbdSDonggeun Kim cm->charger_enabled = enable; 4153bb3dbbdSDonggeun Kim 4163bb3dbbdSDonggeun Kim return err; 4173bb3dbbdSDonggeun Kim } 4183bb3dbbdSDonggeun Kim 4193bb3dbbdSDonggeun Kim /** 4208fcfe088SChanwoo Choi * check_charging_duration - Monitor charging/discharging duration 4218fcfe088SChanwoo Choi * @cm: the Charger Manager representing the battery. 4228fcfe088SChanwoo Choi * 4238fcfe088SChanwoo Choi * If whole charging duration exceed 'charging_max_duration_ms', 4248fcfe088SChanwoo Choi * cm stop charging to prevent overcharge/overheat. If discharging 4258fcfe088SChanwoo Choi * duration exceed 'discharging _max_duration_ms', charger cable is 4268fcfe088SChanwoo Choi * attached, after full-batt, cm start charging to maintain fully 4278fcfe088SChanwoo Choi * charged state for battery. 4288fcfe088SChanwoo Choi */ 4298fcfe088SChanwoo Choi static int check_charging_duration(struct charger_manager *cm) 4308fcfe088SChanwoo Choi { 4318fcfe088SChanwoo Choi struct charger_desc *desc = cm->desc; 4328fcfe088SChanwoo Choi u64 curr = ktime_to_ms(ktime_get()); 4338fcfe088SChanwoo Choi u64 duration; 4348fcfe088SChanwoo Choi int ret = false; 4358fcfe088SChanwoo Choi 4368fcfe088SChanwoo Choi if (!desc->charging_max_duration_ms && 4378fcfe088SChanwoo Choi !desc->discharging_max_duration_ms) 4388fcfe088SChanwoo Choi return ret; 4398fcfe088SChanwoo Choi 4408fcfe088SChanwoo Choi if (cm->charger_enabled) { 4418fcfe088SChanwoo Choi duration = curr - cm->charging_start_time; 4428fcfe088SChanwoo Choi 4438fcfe088SChanwoo Choi if (duration > desc->charging_max_duration_ms) { 444856ee611SJonghwa Lee dev_info(cm->dev, "Charging duration exceed %ums\n", 4458fcfe088SChanwoo Choi desc->charging_max_duration_ms); 4468fcfe088SChanwoo Choi ret = true; 4478fcfe088SChanwoo Choi } 448e132fc6bSJonghwa Lee } else if (cm->battery_status == POWER_SUPPLY_STATUS_NOT_CHARGING) { 4498fcfe088SChanwoo Choi duration = curr - cm->charging_end_time; 4508fcfe088SChanwoo Choi 451e132fc6bSJonghwa Lee if (duration > desc->charging_max_duration_ms) { 452856ee611SJonghwa Lee dev_info(cm->dev, "Discharging duration exceed %ums\n", 4538fcfe088SChanwoo Choi desc->discharging_max_duration_ms); 4548fcfe088SChanwoo Choi ret = true; 4558fcfe088SChanwoo Choi } 4568fcfe088SChanwoo Choi } 4578fcfe088SChanwoo Choi 4588fcfe088SChanwoo Choi return ret; 4598fcfe088SChanwoo Choi } 4608fcfe088SChanwoo Choi 461bdbe8144SKrzysztof Kozlowski static int cm_get_battery_temperature_by_psy(struct charger_manager *cm, 462bdbe8144SKrzysztof Kozlowski int *temp) 463bdbe8144SKrzysztof Kozlowski { 464bdbe8144SKrzysztof Kozlowski struct power_supply *fuel_gauge; 465b43eb35aSKrzysztof Kozlowski int ret; 466bdbe8144SKrzysztof Kozlowski 467bdbe8144SKrzysztof Kozlowski fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge); 468bdbe8144SKrzysztof Kozlowski if (!fuel_gauge) 469bdbe8144SKrzysztof Kozlowski return -ENODEV; 470bdbe8144SKrzysztof Kozlowski 471b43eb35aSKrzysztof Kozlowski ret = power_supply_get_property(fuel_gauge, 472bdbe8144SKrzysztof Kozlowski POWER_SUPPLY_PROP_TEMP, 473bdbe8144SKrzysztof Kozlowski (union power_supply_propval *)temp); 474b43eb35aSKrzysztof Kozlowski power_supply_put(fuel_gauge); 475b43eb35aSKrzysztof Kozlowski 476b43eb35aSKrzysztof Kozlowski return ret; 477bdbe8144SKrzysztof Kozlowski } 478bdbe8144SKrzysztof Kozlowski 4795c49a625SJonghwa Lee static int cm_get_battery_temperature(struct charger_manager *cm, 4805c49a625SJonghwa Lee int *temp) 4815c49a625SJonghwa Lee { 4825c49a625SJonghwa Lee int ret; 4835c49a625SJonghwa Lee 4845c49a625SJonghwa Lee if (!cm->desc->measure_battery_temp) 4855c49a625SJonghwa Lee return -ENODEV; 4865c49a625SJonghwa Lee 4875c49a625SJonghwa Lee #ifdef CONFIG_THERMAL 488bdbe8144SKrzysztof Kozlowski if (cm->tzd_batt) { 48917e8351aSSascha Hauer ret = thermal_zone_get_temp(cm->tzd_batt, temp); 4905c49a625SJonghwa Lee if (!ret) 4915c49a625SJonghwa Lee /* Calibrate temperature unit */ 4925c49a625SJonghwa Lee *temp /= 100; 493bdbe8144SKrzysztof Kozlowski } else 4945c49a625SJonghwa Lee #endif 495bdbe8144SKrzysztof Kozlowski { 496bdbe8144SKrzysztof Kozlowski /* if-else continued from CONFIG_THERMAL */ 497bdbe8144SKrzysztof Kozlowski ret = cm_get_battery_temperature_by_psy(cm, temp); 498bdbe8144SKrzysztof Kozlowski } 499bdbe8144SKrzysztof Kozlowski 5005c49a625SJonghwa Lee return ret; 5015c49a625SJonghwa Lee } 5025c49a625SJonghwa Lee 5035c49a625SJonghwa Lee static int cm_check_thermal_status(struct charger_manager *cm) 5045c49a625SJonghwa Lee { 5055c49a625SJonghwa Lee struct charger_desc *desc = cm->desc; 5065c49a625SJonghwa Lee int temp, upper_limit, lower_limit; 5075c49a625SJonghwa Lee int ret = 0; 5085c49a625SJonghwa Lee 5095c49a625SJonghwa Lee ret = cm_get_battery_temperature(cm, &temp); 5105c49a625SJonghwa Lee if (ret) { 5115c49a625SJonghwa Lee /* FIXME: 5125c49a625SJonghwa Lee * No information of battery temperature might 51302276af2SKrzysztof Kozlowski * occur hazardous result. We have to handle it 5145c49a625SJonghwa Lee * depending on battery type. 5155c49a625SJonghwa Lee */ 5165c49a625SJonghwa Lee dev_err(cm->dev, "Failed to get battery temperature\n"); 5175c49a625SJonghwa Lee return 0; 5185c49a625SJonghwa Lee } 5195c49a625SJonghwa Lee 5205c49a625SJonghwa Lee upper_limit = desc->temp_max; 5215c49a625SJonghwa Lee lower_limit = desc->temp_min; 5225c49a625SJonghwa Lee 5235c49a625SJonghwa Lee if (cm->emergency_stop) { 5245c49a625SJonghwa Lee upper_limit -= desc->temp_diff; 5255c49a625SJonghwa Lee lower_limit += desc->temp_diff; 5265c49a625SJonghwa Lee } 5275c49a625SJonghwa Lee 5285c49a625SJonghwa Lee if (temp > upper_limit) 5299584051fSJonghwa Lee ret = CM_BATT_OVERHEAT; 5305c49a625SJonghwa Lee else if (temp < lower_limit) 5319584051fSJonghwa Lee ret = CM_BATT_COLD; 5329584051fSJonghwa Lee else 5339584051fSJonghwa Lee ret = CM_BATT_OK; 5345c49a625SJonghwa Lee 535e132fc6bSJonghwa Lee cm->emergency_stop = ret; 536e132fc6bSJonghwa Lee 5375c49a625SJonghwa Lee return ret; 5385c49a625SJonghwa Lee } 5395c49a625SJonghwa Lee 5408fcfe088SChanwoo Choi /** 541e132fc6bSJonghwa Lee * cm_get_target_status - Check current status and get next target status. 542e132fc6bSJonghwa Lee * @cm: the Charger Manager representing the battery. 543e132fc6bSJonghwa Lee */ 544e132fc6bSJonghwa Lee static int cm_get_target_status(struct charger_manager *cm) 545e132fc6bSJonghwa Lee { 546e132fc6bSJonghwa Lee if (!is_ext_pwr_online(cm)) 547e132fc6bSJonghwa Lee return POWER_SUPPLY_STATUS_DISCHARGING; 548e132fc6bSJonghwa Lee 549e132fc6bSJonghwa Lee if (cm_check_thermal_status(cm)) { 550e132fc6bSJonghwa Lee /* Check if discharging duration exeeds limit. */ 551e132fc6bSJonghwa Lee if (check_charging_duration(cm)) 552e132fc6bSJonghwa Lee goto charging_ok; 553e132fc6bSJonghwa Lee return POWER_SUPPLY_STATUS_NOT_CHARGING; 554e132fc6bSJonghwa Lee } 555e132fc6bSJonghwa Lee 556e132fc6bSJonghwa Lee switch (cm->battery_status) { 557e132fc6bSJonghwa Lee case POWER_SUPPLY_STATUS_CHARGING: 558e132fc6bSJonghwa Lee /* Check if charging duration exeeds limit. */ 559e132fc6bSJonghwa Lee if (check_charging_duration(cm)) 560e132fc6bSJonghwa Lee return POWER_SUPPLY_STATUS_FULL; 561e132fc6bSJonghwa Lee fallthrough; 562e132fc6bSJonghwa Lee case POWER_SUPPLY_STATUS_FULL: 563e132fc6bSJonghwa Lee if (is_full_charged(cm)) 564e132fc6bSJonghwa Lee return POWER_SUPPLY_STATUS_FULL; 565e132fc6bSJonghwa Lee fallthrough; 566e132fc6bSJonghwa Lee default: 567e132fc6bSJonghwa Lee break; 568e132fc6bSJonghwa Lee } 569e132fc6bSJonghwa Lee 570e132fc6bSJonghwa Lee charging_ok: 571e132fc6bSJonghwa Lee /* Charging is allowed. */ 572e132fc6bSJonghwa Lee return POWER_SUPPLY_STATUS_CHARGING; 573e132fc6bSJonghwa Lee } 574e132fc6bSJonghwa Lee 575e132fc6bSJonghwa Lee /** 5763bb3dbbdSDonggeun Kim * _cm_monitor - Monitor the temperature and return true for exceptions. 5773bb3dbbdSDonggeun Kim * @cm: the Charger Manager representing the battery. 5783bb3dbbdSDonggeun Kim * 5793bb3dbbdSDonggeun Kim * Returns true if there is an event to notify for the battery. 5803bb3dbbdSDonggeun Kim * (True if the status of "emergency_stop" changes) 5813bb3dbbdSDonggeun Kim */ 5823bb3dbbdSDonggeun Kim static bool _cm_monitor(struct charger_manager *cm) 5833bb3dbbdSDonggeun Kim { 584e132fc6bSJonghwa Lee int target; 5853bb3dbbdSDonggeun Kim 586e132fc6bSJonghwa Lee target = cm_get_target_status(cm); 5873bb3dbbdSDonggeun Kim 588e132fc6bSJonghwa Lee try_charger_enable(cm, (target == POWER_SUPPLY_STATUS_CHARGING)); 5893bb3dbbdSDonggeun Kim 590e132fc6bSJonghwa Lee if (cm->battery_status != target) { 591e132fc6bSJonghwa Lee cm->battery_status = target; 592e132fc6bSJonghwa Lee power_supply_changed(cm->charger_psy); 5932ed9e9b6SChanwoo Choi } 5943bb3dbbdSDonggeun Kim 595e132fc6bSJonghwa Lee return (cm->battery_status == POWER_SUPPLY_STATUS_NOT_CHARGING); 5963bb3dbbdSDonggeun Kim } 5973bb3dbbdSDonggeun Kim 5983bb3dbbdSDonggeun Kim /** 5993bb3dbbdSDonggeun Kim * cm_monitor - Monitor every battery. 6003bb3dbbdSDonggeun Kim * 6013bb3dbbdSDonggeun Kim * Returns true if there is an event to notify from any of the batteries. 6023bb3dbbdSDonggeun Kim * (True if the status of "emergency_stop" changes) 6033bb3dbbdSDonggeun Kim */ 6043bb3dbbdSDonggeun Kim static bool cm_monitor(void) 6053bb3dbbdSDonggeun Kim { 6063bb3dbbdSDonggeun Kim bool stop = false; 6073bb3dbbdSDonggeun Kim struct charger_manager *cm; 6083bb3dbbdSDonggeun Kim 6093bb3dbbdSDonggeun Kim mutex_lock(&cm_list_mtx); 6103bb3dbbdSDonggeun Kim 611bb2a95c2SAxel Lin list_for_each_entry(cm, &cm_list, entry) { 612bb2a95c2SAxel Lin if (_cm_monitor(cm)) 613bb2a95c2SAxel Lin stop = true; 614bb2a95c2SAxel Lin } 6153bb3dbbdSDonggeun Kim 6163bb3dbbdSDonggeun Kim mutex_unlock(&cm_list_mtx); 6173bb3dbbdSDonggeun Kim 6183bb3dbbdSDonggeun Kim return stop; 6193bb3dbbdSDonggeun Kim } 6203bb3dbbdSDonggeun Kim 621d829dc75SChanwoo Choi /** 622d829dc75SChanwoo Choi * _setup_polling - Setup the next instance of polling. 623d829dc75SChanwoo Choi * @work: work_struct of the function _setup_polling. 624d829dc75SChanwoo Choi */ 625d829dc75SChanwoo Choi static void _setup_polling(struct work_struct *work) 626d829dc75SChanwoo Choi { 627d829dc75SChanwoo Choi unsigned long min = ULONG_MAX; 628d829dc75SChanwoo Choi struct charger_manager *cm; 629d829dc75SChanwoo Choi bool keep_polling = false; 630d829dc75SChanwoo Choi unsigned long _next_polling; 631d829dc75SChanwoo Choi 632d829dc75SChanwoo Choi mutex_lock(&cm_list_mtx); 633d829dc75SChanwoo Choi 634d829dc75SChanwoo Choi list_for_each_entry(cm, &cm_list, entry) { 635d829dc75SChanwoo Choi if (is_polling_required(cm) && cm->desc->polling_interval_ms) { 636d829dc75SChanwoo Choi keep_polling = true; 637d829dc75SChanwoo Choi 638d829dc75SChanwoo Choi if (min > cm->desc->polling_interval_ms) 639d829dc75SChanwoo Choi min = cm->desc->polling_interval_ms; 640d829dc75SChanwoo Choi } 641d829dc75SChanwoo Choi } 642d829dc75SChanwoo Choi 643d829dc75SChanwoo Choi polling_jiffy = msecs_to_jiffies(min); 644d829dc75SChanwoo Choi if (polling_jiffy <= CM_JIFFIES_SMALL) 645d829dc75SChanwoo Choi polling_jiffy = CM_JIFFIES_SMALL + 1; 646d829dc75SChanwoo Choi 647d829dc75SChanwoo Choi if (!keep_polling) 648d829dc75SChanwoo Choi polling_jiffy = ULONG_MAX; 649d829dc75SChanwoo Choi if (polling_jiffy == ULONG_MAX) 650d829dc75SChanwoo Choi goto out; 651d829dc75SChanwoo Choi 652d829dc75SChanwoo Choi WARN(cm_wq == NULL, "charger-manager: workqueue not initialized" 653d829dc75SChanwoo Choi ". try it later. %s\n", __func__); 654d829dc75SChanwoo Choi 6552fbb520dSTejun Heo /* 6562fbb520dSTejun Heo * Use mod_delayed_work() iff the next polling interval should 6572fbb520dSTejun Heo * occur before the currently scheduled one. If @cm_monitor_work 6582fbb520dSTejun Heo * isn't active, the end result is the same, so no need to worry 6592fbb520dSTejun Heo * about stale @next_polling. 6602fbb520dSTejun Heo */ 661d829dc75SChanwoo Choi _next_polling = jiffies + polling_jiffy; 662d829dc75SChanwoo Choi 6632fbb520dSTejun Heo if (time_before(_next_polling, next_polling)) { 66441f63c53STejun Heo mod_delayed_work(cm_wq, &cm_monitor_work, polling_jiffy); 6652fbb520dSTejun Heo next_polling = _next_polling; 6662fbb520dSTejun Heo } else { 6672fbb520dSTejun Heo if (queue_delayed_work(cm_wq, &cm_monitor_work, polling_jiffy)) 6682fbb520dSTejun Heo next_polling = _next_polling; 669d829dc75SChanwoo Choi } 670d829dc75SChanwoo Choi out: 671d829dc75SChanwoo Choi mutex_unlock(&cm_list_mtx); 672d829dc75SChanwoo Choi } 673d829dc75SChanwoo Choi static DECLARE_WORK(setup_polling, _setup_polling); 674d829dc75SChanwoo Choi 675d829dc75SChanwoo Choi /** 676d829dc75SChanwoo Choi * cm_monitor_poller - The Monitor / Poller. 677d829dc75SChanwoo Choi * @work: work_struct of the function cm_monitor_poller 678d829dc75SChanwoo Choi * 679d829dc75SChanwoo Choi * During non-suspended state, cm_monitor_poller is used to poll and monitor 680d829dc75SChanwoo Choi * the batteries. 681d829dc75SChanwoo Choi */ 682d829dc75SChanwoo Choi static void cm_monitor_poller(struct work_struct *work) 683d829dc75SChanwoo Choi { 684d829dc75SChanwoo Choi cm_monitor(); 685d829dc75SChanwoo Choi schedule_work(&setup_polling); 686d829dc75SChanwoo Choi } 687d829dc75SChanwoo Choi 688ad3d13eeSDonggeun Kim static int charger_get_property(struct power_supply *psy, 689ad3d13eeSDonggeun Kim enum power_supply_property psp, 690ad3d13eeSDonggeun Kim union power_supply_propval *val) 691ad3d13eeSDonggeun Kim { 692297d716fSKrzysztof Kozlowski struct charger_manager *cm = power_supply_get_drvdata(psy); 693ad3d13eeSDonggeun Kim struct charger_desc *desc = cm->desc; 694b43eb35aSKrzysztof Kozlowski struct power_supply *fuel_gauge = NULL; 695df58c04cSAnton Vorontsov int ret = 0; 696df58c04cSAnton Vorontsov int uV; 697ad3d13eeSDonggeun Kim 698ad3d13eeSDonggeun Kim switch (psp) { 699ad3d13eeSDonggeun Kim case POWER_SUPPLY_PROP_STATUS: 700e132fc6bSJonghwa Lee val->intval = cm->battery_status; 701ad3d13eeSDonggeun Kim break; 702ad3d13eeSDonggeun Kim case POWER_SUPPLY_PROP_HEALTH: 703ad3d13eeSDonggeun Kim if (cm->emergency_stop > 0) 704ad3d13eeSDonggeun Kim val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; 705ad3d13eeSDonggeun Kim else if (cm->emergency_stop < 0) 706ad3d13eeSDonggeun Kim val->intval = POWER_SUPPLY_HEALTH_COLD; 707ad3d13eeSDonggeun Kim else 708ad3d13eeSDonggeun Kim val->intval = POWER_SUPPLY_HEALTH_GOOD; 709ad3d13eeSDonggeun Kim break; 710ad3d13eeSDonggeun Kim case POWER_SUPPLY_PROP_PRESENT: 711ad3d13eeSDonggeun Kim if (is_batt_present(cm)) 712ad3d13eeSDonggeun Kim val->intval = 1; 713ad3d13eeSDonggeun Kim else 714ad3d13eeSDonggeun Kim val->intval = 0; 715ad3d13eeSDonggeun Kim break; 716ad3d13eeSDonggeun Kim case POWER_SUPPLY_PROP_VOLTAGE_NOW: 717df58c04cSAnton Vorontsov ret = get_batt_uV(cm, &val->intval); 718ad3d13eeSDonggeun Kim break; 719ad3d13eeSDonggeun Kim case POWER_SUPPLY_PROP_CURRENT_NOW: 720bdbe8144SKrzysztof Kozlowski fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge); 721bdbe8144SKrzysztof Kozlowski if (!fuel_gauge) { 722bdbe8144SKrzysztof Kozlowski ret = -ENODEV; 723bdbe8144SKrzysztof Kozlowski break; 724bdbe8144SKrzysztof Kozlowski } 725b70229bcSKrzysztof Kozlowski ret = power_supply_get_property(fuel_gauge, 726ad3d13eeSDonggeun Kim POWER_SUPPLY_PROP_CURRENT_NOW, val); 727ad3d13eeSDonggeun Kim break; 728ad3d13eeSDonggeun Kim case POWER_SUPPLY_PROP_TEMP: 7295c49a625SJonghwa Lee return cm_get_battery_temperature(cm, &val->intval); 730ad3d13eeSDonggeun Kim case POWER_SUPPLY_PROP_CAPACITY: 731ad3d13eeSDonggeun Kim if (!is_batt_present(cm)) { 732ad3d13eeSDonggeun Kim /* There is no battery. Assume 100% */ 733ad3d13eeSDonggeun Kim val->intval = 100; 734ad3d13eeSDonggeun Kim break; 735ad3d13eeSDonggeun Kim } 736ad3d13eeSDonggeun Kim 737b43eb35aSKrzysztof Kozlowski fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge); 738b43eb35aSKrzysztof Kozlowski if (!fuel_gauge) { 739b43eb35aSKrzysztof Kozlowski ret = -ENODEV; 740b43eb35aSKrzysztof Kozlowski break; 741b43eb35aSKrzysztof Kozlowski } 742b43eb35aSKrzysztof Kozlowski 743b70229bcSKrzysztof Kozlowski ret = power_supply_get_property(fuel_gauge, 744ad3d13eeSDonggeun Kim POWER_SUPPLY_PROP_CAPACITY, val); 745ad3d13eeSDonggeun Kim if (ret) 746ad3d13eeSDonggeun Kim break; 747ad3d13eeSDonggeun Kim 748ad3d13eeSDonggeun Kim if (val->intval > 100) { 749ad3d13eeSDonggeun Kim val->intval = 100; 750ad3d13eeSDonggeun Kim break; 751ad3d13eeSDonggeun Kim } 752ad3d13eeSDonggeun Kim if (val->intval < 0) 753ad3d13eeSDonggeun Kim val->intval = 0; 754ad3d13eeSDonggeun Kim 755ad3d13eeSDonggeun Kim /* Do not adjust SOC when charging: voltage is overrated */ 756ad3d13eeSDonggeun Kim if (is_charging(cm)) 757ad3d13eeSDonggeun Kim break; 758ad3d13eeSDonggeun Kim 759ad3d13eeSDonggeun Kim /* 760ad3d13eeSDonggeun Kim * If the capacity value is inconsistent, calibrate it base on 761ad3d13eeSDonggeun Kim * the battery voltage values and the thresholds given as desc 762ad3d13eeSDonggeun Kim */ 763ad3d13eeSDonggeun Kim ret = get_batt_uV(cm, &uV); 764ad3d13eeSDonggeun Kim if (ret) { 765ad3d13eeSDonggeun Kim /* Voltage information not available. No calibration */ 766ad3d13eeSDonggeun Kim ret = 0; 767ad3d13eeSDonggeun Kim break; 768ad3d13eeSDonggeun Kim } 769ad3d13eeSDonggeun Kim 770ad3d13eeSDonggeun Kim if (desc->fullbatt_uV > 0 && uV >= desc->fullbatt_uV && 771ad3d13eeSDonggeun Kim !is_charging(cm)) { 772ad3d13eeSDonggeun Kim val->intval = 100; 773ad3d13eeSDonggeun Kim break; 774ad3d13eeSDonggeun Kim } 775ad3d13eeSDonggeun Kim 776ad3d13eeSDonggeun Kim break; 777ad3d13eeSDonggeun Kim case POWER_SUPPLY_PROP_ONLINE: 778ad3d13eeSDonggeun Kim if (is_ext_pwr_online(cm)) 779ad3d13eeSDonggeun Kim val->intval = 1; 780ad3d13eeSDonggeun Kim else 781ad3d13eeSDonggeun Kim val->intval = 0; 782ad3d13eeSDonggeun Kim break; 783ad3d13eeSDonggeun Kim case POWER_SUPPLY_PROP_CHARGE_FULL: 784ad3d13eeSDonggeun Kim case POWER_SUPPLY_PROP_CHARGE_NOW: 7850a9e0f94SJonghwa Lee fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge); 786bdbe8144SKrzysztof Kozlowski if (!fuel_gauge) { 787bdbe8144SKrzysztof Kozlowski ret = -ENODEV; 788bdbe8144SKrzysztof Kozlowski break; 789bdbe8144SKrzysztof Kozlowski } 7900a9e0f94SJonghwa Lee ret = power_supply_get_property(fuel_gauge, psp, val); 791ad3d13eeSDonggeun Kim break; 792ad3d13eeSDonggeun Kim default: 793ad3d13eeSDonggeun Kim return -EINVAL; 794ad3d13eeSDonggeun Kim } 795b43eb35aSKrzysztof Kozlowski if (fuel_gauge) 796b43eb35aSKrzysztof Kozlowski power_supply_put(fuel_gauge); 797ad3d13eeSDonggeun Kim return ret; 798ad3d13eeSDonggeun Kim } 799ad3d13eeSDonggeun Kim 800ad3d13eeSDonggeun Kim #define NUM_CHARGER_PSY_OPTIONAL (4) 801ad3d13eeSDonggeun Kim static enum power_supply_property default_charger_props[] = { 802ad3d13eeSDonggeun Kim /* Guaranteed to provide */ 803ad3d13eeSDonggeun Kim POWER_SUPPLY_PROP_STATUS, 804ad3d13eeSDonggeun Kim POWER_SUPPLY_PROP_HEALTH, 805ad3d13eeSDonggeun Kim POWER_SUPPLY_PROP_PRESENT, 806ad3d13eeSDonggeun Kim POWER_SUPPLY_PROP_VOLTAGE_NOW, 807ad3d13eeSDonggeun Kim POWER_SUPPLY_PROP_CAPACITY, 808ad3d13eeSDonggeun Kim POWER_SUPPLY_PROP_ONLINE, 809ad3d13eeSDonggeun Kim /* 810ad3d13eeSDonggeun Kim * Optional properties are: 8110a9e0f94SJonghwa Lee * POWER_SUPPLY_PROP_CHARGE_FULL, 812ad3d13eeSDonggeun Kim * POWER_SUPPLY_PROP_CHARGE_NOW, 813ad3d13eeSDonggeun Kim * POWER_SUPPLY_PROP_CURRENT_NOW, 814cdaeb151SJonathan Bakker * POWER_SUPPLY_PROP_TEMP, 815ad3d13eeSDonggeun Kim */ 816ad3d13eeSDonggeun Kim }; 817ad3d13eeSDonggeun Kim 818297d716fSKrzysztof Kozlowski static const struct power_supply_desc psy_default = { 819ad3d13eeSDonggeun Kim .name = "battery", 820ad3d13eeSDonggeun Kim .type = POWER_SUPPLY_TYPE_BATTERY, 821ad3d13eeSDonggeun Kim .properties = default_charger_props, 822ad3d13eeSDonggeun Kim .num_properties = ARRAY_SIZE(default_charger_props), 823ad3d13eeSDonggeun Kim .get_property = charger_get_property, 824ba9c9182SKrzysztof Kozlowski .no_thermal = true, 825ad3d13eeSDonggeun Kim }; 826ad3d13eeSDonggeun Kim 8273bb3dbbdSDonggeun Kim /** 8283bb3dbbdSDonggeun Kim * cm_setup_timer - For in-suspend monitoring setup wakeup alarm 8293bb3dbbdSDonggeun Kim * for suspend_again. 8303bb3dbbdSDonggeun Kim * 8313bb3dbbdSDonggeun Kim * Returns true if the alarm is set for Charger Manager to use. 8323bb3dbbdSDonggeun Kim * Returns false if 8333bb3dbbdSDonggeun Kim * cm_setup_timer fails to set an alarm, 8343bb3dbbdSDonggeun Kim * cm_setup_timer does not need to set an alarm for Charger Manager, 8353bb3dbbdSDonggeun Kim * or an alarm previously configured is to be used. 8363bb3dbbdSDonggeun Kim */ 8373bb3dbbdSDonggeun Kim static bool cm_setup_timer(void) 8383bb3dbbdSDonggeun Kim { 8393bb3dbbdSDonggeun Kim struct charger_manager *cm; 8403bb3dbbdSDonggeun Kim unsigned int wakeup_ms = UINT_MAX; 841c1155c64SJonghwa Lee int timer_req = 0; 842c1155c64SJonghwa Lee 843c1155c64SJonghwa Lee if (time_after(next_polling, jiffies)) 844c1155c64SJonghwa Lee CM_MIN_VALID(wakeup_ms, 845c1155c64SJonghwa Lee jiffies_to_msecs(next_polling - jiffies)); 8463bb3dbbdSDonggeun Kim 8473bb3dbbdSDonggeun Kim mutex_lock(&cm_list_mtx); 8483bb3dbbdSDonggeun Kim list_for_each_entry(cm, &cm_list, entry) { 8493bb3dbbdSDonggeun Kim /* Skip if polling is not required for this CM */ 8503bb3dbbdSDonggeun Kim if (!is_polling_required(cm) && !cm->emergency_stop) 8513bb3dbbdSDonggeun Kim continue; 852c1155c64SJonghwa Lee timer_req++; 8533bb3dbbdSDonggeun Kim if (cm->desc->polling_interval_ms == 0) 8543bb3dbbdSDonggeun Kim continue; 8553bb3dbbdSDonggeun Kim CM_MIN_VALID(wakeup_ms, cm->desc->polling_interval_ms); 8563bb3dbbdSDonggeun Kim } 8573bb3dbbdSDonggeun Kim mutex_unlock(&cm_list_mtx); 8583bb3dbbdSDonggeun Kim 859c1155c64SJonghwa Lee if (timer_req && cm_timer) { 860c1155c64SJonghwa Lee ktime_t now, add; 8613bb3dbbdSDonggeun Kim 8623bb3dbbdSDonggeun Kim /* 8633bb3dbbdSDonggeun Kim * Set alarm with the polling interval (wakeup_ms) 864c1155c64SJonghwa Lee * The alarm time should be NOW + CM_RTC_SMALL or later. 8653bb3dbbdSDonggeun Kim */ 866c1155c64SJonghwa Lee if (wakeup_ms == UINT_MAX || 867c1155c64SJonghwa Lee wakeup_ms < CM_RTC_SMALL * MSEC_PER_SEC) 868c1155c64SJonghwa Lee wakeup_ms = 2 * CM_RTC_SMALL * MSEC_PER_SEC; 8693bb3dbbdSDonggeun Kim 870c1155c64SJonghwa Lee pr_info("Charger Manager wakeup timer: %u ms\n", wakeup_ms); 8713bb3dbbdSDonggeun Kim 872c1155c64SJonghwa Lee now = ktime_get_boottime(); 873c1155c64SJonghwa Lee add = ktime_set(wakeup_ms / MSEC_PER_SEC, 874c1155c64SJonghwa Lee (wakeup_ms % MSEC_PER_SEC) * NSEC_PER_MSEC); 875c1155c64SJonghwa Lee alarm_start(cm_timer, ktime_add(now, add)); 8763bb3dbbdSDonggeun Kim 877c1155c64SJonghwa Lee cm_suspend_duration_ms = wakeup_ms; 878c1155c64SJonghwa Lee 879c1155c64SJonghwa Lee return true; 8803bb3dbbdSDonggeun Kim } 8813bb3dbbdSDonggeun Kim return false; 8823bb3dbbdSDonggeun Kim } 8833bb3dbbdSDonggeun Kim 884bee737bcSChanwoo Choi /** 885bee737bcSChanwoo Choi * charger_extcon_work - enable/diable charger according to the state 886bee737bcSChanwoo Choi * of charger cable 887bee737bcSChanwoo Choi * 888bee737bcSChanwoo Choi * @work: work_struct of the function charger_extcon_work. 889bee737bcSChanwoo Choi */ 890bee737bcSChanwoo Choi static void charger_extcon_work(struct work_struct *work) 891bee737bcSChanwoo Choi { 892bee737bcSChanwoo Choi struct charger_cable *cable = 893bee737bcSChanwoo Choi container_of(work, struct charger_cable, wq); 89445cd4fb2SChanwoo Choi int ret; 89545cd4fb2SChanwoo Choi 89645cd4fb2SChanwoo Choi if (cable->attached && cable->min_uA != 0 && cable->max_uA != 0) { 89745cd4fb2SChanwoo Choi ret = regulator_set_current_limit(cable->charger->consumer, 89845cd4fb2SChanwoo Choi cable->min_uA, cable->max_uA); 89945cd4fb2SChanwoo Choi if (ret < 0) { 90045cd4fb2SChanwoo Choi pr_err("Cannot set current limit of %s (%s)\n", 90145cd4fb2SChanwoo Choi cable->charger->regulator_name, cable->name); 90245cd4fb2SChanwoo Choi return; 90345cd4fb2SChanwoo Choi } 90445cd4fb2SChanwoo Choi 90545cd4fb2SChanwoo Choi pr_info("Set current limit of %s : %duA ~ %duA\n", 90645cd4fb2SChanwoo Choi cable->charger->regulator_name, 90745cd4fb2SChanwoo Choi cable->min_uA, cable->max_uA); 90845cd4fb2SChanwoo Choi } 909bee737bcSChanwoo Choi 9109434e453SJonghwa Lee cancel_delayed_work(&cm_monitor_work); 9119434e453SJonghwa Lee queue_delayed_work(cm_wq, &cm_monitor_work, 0); 912bee737bcSChanwoo Choi } 913bee737bcSChanwoo Choi 914bee737bcSChanwoo Choi /** 915bee737bcSChanwoo Choi * charger_extcon_notifier - receive the state of charger cable 916bee737bcSChanwoo Choi * when registered cable is attached or detached. 917bee737bcSChanwoo Choi * 918bee737bcSChanwoo Choi * @self: the notifier block of the charger_extcon_notifier. 919bee737bcSChanwoo Choi * @event: the cable state. 920bee737bcSChanwoo Choi * @ptr: the data pointer of notifier block. 921bee737bcSChanwoo Choi */ 922bee737bcSChanwoo Choi static int charger_extcon_notifier(struct notifier_block *self, 923bee737bcSChanwoo Choi unsigned long event, void *ptr) 924bee737bcSChanwoo Choi { 925bee737bcSChanwoo Choi struct charger_cable *cable = 926bee737bcSChanwoo Choi container_of(self, struct charger_cable, nb); 927bee737bcSChanwoo Choi 9282ed9e9b6SChanwoo Choi /* 9292ed9e9b6SChanwoo Choi * The newly state of charger cable. 9302ed9e9b6SChanwoo Choi * If cable is attached, cable->attached is true. 9312ed9e9b6SChanwoo Choi */ 932bee737bcSChanwoo Choi cable->attached = event; 9332ed9e9b6SChanwoo Choi 9342ed9e9b6SChanwoo Choi /* 9352ed9e9b6SChanwoo Choi * Setup work for controlling charger(regulator) 9362ed9e9b6SChanwoo Choi * according to charger cable. 9372ed9e9b6SChanwoo Choi */ 938bee737bcSChanwoo Choi schedule_work(&cable->wq); 939bee737bcSChanwoo Choi 940bee737bcSChanwoo Choi return NOTIFY_DONE; 941bee737bcSChanwoo Choi } 942bee737bcSChanwoo Choi 943bee737bcSChanwoo Choi /** 944bee737bcSChanwoo Choi * charger_extcon_init - register external connector to use it 945bee737bcSChanwoo Choi * as the charger cable 946bee737bcSChanwoo Choi * 947bee737bcSChanwoo Choi * @cm: the Charger Manager representing the battery. 948bee737bcSChanwoo Choi * @cable: the Charger cable representing the external connector. 949bee737bcSChanwoo Choi */ 950bee737bcSChanwoo Choi static int charger_extcon_init(struct charger_manager *cm, 951bee737bcSChanwoo Choi struct charger_cable *cable) 952bee737bcSChanwoo Choi { 953dc6ea7d4SAndi Shyti int ret; 954bee737bcSChanwoo Choi 955bee737bcSChanwoo Choi /* 956bee737bcSChanwoo Choi * Charger manager use Extcon framework to identify 957bee737bcSChanwoo Choi * the charger cable among various external connector 958bee737bcSChanwoo Choi * cable (e.g., TA, USB, MHL, Dock). 959bee737bcSChanwoo Choi */ 960bee737bcSChanwoo Choi INIT_WORK(&cable->wq, charger_extcon_work); 961bee737bcSChanwoo Choi cable->nb.notifier_call = charger_extcon_notifier; 962bee737bcSChanwoo Choi ret = extcon_register_interest(&cable->extcon_dev, 963bee737bcSChanwoo Choi cable->extcon_name, cable->name, &cable->nb); 964bee737bcSChanwoo Choi if (ret < 0) { 965e5409cbdSJoe Perches pr_info("Cannot register extcon_dev for %s(cable: %s)\n", 966e5409cbdSJoe Perches cable->extcon_name, cable->name); 967bee737bcSChanwoo Choi } 968bee737bcSChanwoo Choi 969bee737bcSChanwoo Choi return ret; 970bee737bcSChanwoo Choi } 971bee737bcSChanwoo Choi 97241468a11SChanwoo Choi /** 97302276af2SKrzysztof Kozlowski * charger_manager_register_extcon - Register extcon device to receive state 97441468a11SChanwoo Choi * of charger cable. 97541468a11SChanwoo Choi * @cm: the Charger Manager representing the battery. 97641468a11SChanwoo Choi * 97741468a11SChanwoo Choi * This function support EXTCON(External Connector) subsystem to detect the 97841468a11SChanwoo Choi * state of charger cables for enabling or disabling charger(regulator) and 97941468a11SChanwoo Choi * select the charger cable for charging among a number of external cable 98041468a11SChanwoo Choi * according to policy of H/W board. 98141468a11SChanwoo Choi */ 98241468a11SChanwoo Choi static int charger_manager_register_extcon(struct charger_manager *cm) 98341468a11SChanwoo Choi { 98441468a11SChanwoo Choi struct charger_desc *desc = cm->desc; 98541468a11SChanwoo Choi struct charger_regulator *charger; 986dc6ea7d4SAndi Shyti int ret; 98741468a11SChanwoo Choi int i; 98841468a11SChanwoo Choi int j; 98941468a11SChanwoo Choi 99041468a11SChanwoo Choi for (i = 0; i < desc->num_charger_regulators; i++) { 99141468a11SChanwoo Choi charger = &desc->charger_regulators[i]; 99241468a11SChanwoo Choi 99341468a11SChanwoo Choi charger->consumer = regulator_get(cm->dev, 99441468a11SChanwoo Choi charger->regulator_name); 9955a6c2208SJonghwa Lee if (IS_ERR(charger->consumer)) { 996e5409cbdSJoe Perches dev_err(cm->dev, "Cannot find charger(%s)\n", 99741468a11SChanwoo Choi charger->regulator_name); 9985a6c2208SJonghwa Lee return PTR_ERR(charger->consumer); 99941468a11SChanwoo Choi } 100041468a11SChanwoo Choi charger->cm = cm; 100141468a11SChanwoo Choi 100241468a11SChanwoo Choi for (j = 0; j < charger->num_cables; j++) { 100341468a11SChanwoo Choi struct charger_cable *cable = &charger->cables[j]; 100441468a11SChanwoo Choi 100541468a11SChanwoo Choi ret = charger_extcon_init(cm, cable); 100641468a11SChanwoo Choi if (ret < 0) { 1007e5409cbdSJoe Perches dev_err(cm->dev, "Cannot initialize charger(%s)\n", 100841468a11SChanwoo Choi charger->regulator_name); 1009dc6ea7d4SAndi Shyti return ret; 101041468a11SChanwoo Choi } 101141468a11SChanwoo Choi cable->charger = charger; 101241468a11SChanwoo Choi cable->cm = cm; 101341468a11SChanwoo Choi } 101441468a11SChanwoo Choi } 101541468a11SChanwoo Choi 1016dc6ea7d4SAndi Shyti return 0; 101741468a11SChanwoo Choi } 101841468a11SChanwoo Choi 10193950c786SChanwoo Choi /* help function of sysfs node to control charger(regulator) */ 10203950c786SChanwoo Choi static ssize_t charger_name_show(struct device *dev, 10213950c786SChanwoo Choi struct device_attribute *attr, char *buf) 10223950c786SChanwoo Choi { 10233950c786SChanwoo Choi struct charger_regulator *charger 10243950c786SChanwoo Choi = container_of(attr, struct charger_regulator, attr_name); 10253950c786SChanwoo Choi 10263950c786SChanwoo Choi return sprintf(buf, "%s\n", charger->regulator_name); 10273950c786SChanwoo Choi } 10283950c786SChanwoo Choi 10293950c786SChanwoo Choi static ssize_t charger_state_show(struct device *dev, 10303950c786SChanwoo Choi struct device_attribute *attr, char *buf) 10313950c786SChanwoo Choi { 10323950c786SChanwoo Choi struct charger_regulator *charger 10333950c786SChanwoo Choi = container_of(attr, struct charger_regulator, attr_state); 10343950c786SChanwoo Choi int state = 0; 10353950c786SChanwoo Choi 10363950c786SChanwoo Choi if (!charger->externally_control) 10373950c786SChanwoo Choi state = regulator_is_enabled(charger->consumer); 10383950c786SChanwoo Choi 10393950c786SChanwoo Choi return sprintf(buf, "%s\n", state ? "enabled" : "disabled"); 10403950c786SChanwoo Choi } 10413950c786SChanwoo Choi 10423950c786SChanwoo Choi static ssize_t charger_externally_control_show(struct device *dev, 10433950c786SChanwoo Choi struct device_attribute *attr, char *buf) 10443950c786SChanwoo Choi { 10453950c786SChanwoo Choi struct charger_regulator *charger = container_of(attr, 10463950c786SChanwoo Choi struct charger_regulator, attr_externally_control); 10473950c786SChanwoo Choi 10483950c786SChanwoo Choi return sprintf(buf, "%d\n", charger->externally_control); 10493950c786SChanwoo Choi } 10503950c786SChanwoo Choi 10513950c786SChanwoo Choi static ssize_t charger_externally_control_store(struct device *dev, 10523950c786SChanwoo Choi struct device_attribute *attr, const char *buf, 10533950c786SChanwoo Choi size_t count) 10543950c786SChanwoo Choi { 10553950c786SChanwoo Choi struct charger_regulator *charger 10563950c786SChanwoo Choi = container_of(attr, struct charger_regulator, 10573950c786SChanwoo Choi attr_externally_control); 10583950c786SChanwoo Choi struct charger_manager *cm = charger->cm; 10593950c786SChanwoo Choi struct charger_desc *desc = cm->desc; 10603950c786SChanwoo Choi int i; 10613950c786SChanwoo Choi int ret; 10623950c786SChanwoo Choi int externally_control; 10633950c786SChanwoo Choi int chargers_externally_control = 1; 10643950c786SChanwoo Choi 10653950c786SChanwoo Choi ret = sscanf(buf, "%d", &externally_control); 10663950c786SChanwoo Choi if (ret == 0) { 10673950c786SChanwoo Choi ret = -EINVAL; 10683950c786SChanwoo Choi return ret; 10693950c786SChanwoo Choi } 10703950c786SChanwoo Choi 10713950c786SChanwoo Choi if (!externally_control) { 10723950c786SChanwoo Choi charger->externally_control = 0; 10733950c786SChanwoo Choi return count; 10743950c786SChanwoo Choi } 10753950c786SChanwoo Choi 10763950c786SChanwoo Choi for (i = 0; i < desc->num_charger_regulators; i++) { 10773950c786SChanwoo Choi if (&desc->charger_regulators[i] != charger && 10783950c786SChanwoo Choi !desc->charger_regulators[i].externally_control) { 10793950c786SChanwoo Choi /* 10803950c786SChanwoo Choi * At least, one charger is controlled by 10813950c786SChanwoo Choi * charger-manager 10823950c786SChanwoo Choi */ 10833950c786SChanwoo Choi chargers_externally_control = 0; 10843950c786SChanwoo Choi break; 10853950c786SChanwoo Choi } 10863950c786SChanwoo Choi } 10873950c786SChanwoo Choi 10883950c786SChanwoo Choi if (!chargers_externally_control) { 10893950c786SChanwoo Choi if (cm->charger_enabled) { 10903950c786SChanwoo Choi try_charger_enable(charger->cm, false); 10913950c786SChanwoo Choi charger->externally_control = externally_control; 10923950c786SChanwoo Choi try_charger_enable(charger->cm, true); 10933950c786SChanwoo Choi } else { 10943950c786SChanwoo Choi charger->externally_control = externally_control; 10953950c786SChanwoo Choi } 10963950c786SChanwoo Choi } else { 10973950c786SChanwoo Choi dev_warn(cm->dev, 1098e5409cbdSJoe Perches "'%s' regulator should be controlled in charger-manager because charger-manager must need at least one charger for charging\n", 10993950c786SChanwoo Choi charger->regulator_name); 11003950c786SChanwoo Choi } 11013950c786SChanwoo Choi 11023950c786SChanwoo Choi return count; 11033950c786SChanwoo Choi } 11043950c786SChanwoo Choi 110541468a11SChanwoo Choi /** 1106157ba1bbSSebastian Reichel * charger_manager_prepare_sysfs - Prepare sysfs entry for each charger 110741468a11SChanwoo Choi * @cm: the Charger Manager representing the battery. 110841468a11SChanwoo Choi * 110941468a11SChanwoo Choi * This function add sysfs entry for charger(regulator) to control charger from 111041468a11SChanwoo Choi * user-space. If some development board use one more chargers for charging 111141468a11SChanwoo Choi * but only need one charger on specific case which is dependent on user 111241468a11SChanwoo Choi * scenario or hardware restrictions, the user enter 1 or 0(zero) to '/sys/ 111341468a11SChanwoo Choi * class/power_supply/battery/charger.[index]/externally_control'. For example, 111441468a11SChanwoo Choi * if user enter 1 to 'sys/class/power_supply/battery/charger.[index]/ 111541468a11SChanwoo Choi * externally_control, this charger isn't controlled from charger-manager and 111641468a11SChanwoo Choi * always stay off state of regulator. 111741468a11SChanwoo Choi */ 1118157ba1bbSSebastian Reichel static int charger_manager_prepare_sysfs(struct charger_manager *cm) 111941468a11SChanwoo Choi { 112041468a11SChanwoo Choi struct charger_desc *desc = cm->desc; 112141468a11SChanwoo Choi struct charger_regulator *charger; 112241468a11SChanwoo Choi int chargers_externally_control = 1; 1123efcca6bdSSebastian Reichel char *name; 112441468a11SChanwoo Choi int i; 112541468a11SChanwoo Choi 112641468a11SChanwoo Choi /* Create sysfs entry to control charger(regulator) */ 112741468a11SChanwoo Choi for (i = 0; i < desc->num_charger_regulators; i++) { 112841468a11SChanwoo Choi charger = &desc->charger_regulators[i]; 112941468a11SChanwoo Choi 1130efcca6bdSSebastian Reichel name = devm_kasprintf(cm->dev, GFP_KERNEL, "charger.%d", i); 1131efcca6bdSSebastian Reichel if (!name) 1132dc6ea7d4SAndi Shyti return -ENOMEM; 1133dc6ea7d4SAndi Shyti 113441468a11SChanwoo Choi charger->attrs[0] = &charger->attr_name.attr; 113541468a11SChanwoo Choi charger->attrs[1] = &charger->attr_state.attr; 113641468a11SChanwoo Choi charger->attrs[2] = &charger->attr_externally_control.attr; 113741468a11SChanwoo Choi charger->attrs[3] = NULL; 1138157ba1bbSSebastian Reichel 1139157ba1bbSSebastian Reichel charger->attr_grp.name = name; 1140157ba1bbSSebastian Reichel charger->attr_grp.attrs = charger->attrs; 1141157ba1bbSSebastian Reichel desc->sysfs_groups[i] = &charger->attr_grp; 114241468a11SChanwoo Choi 114341468a11SChanwoo Choi sysfs_attr_init(&charger->attr_name.attr); 114441468a11SChanwoo Choi charger->attr_name.attr.name = "name"; 114541468a11SChanwoo Choi charger->attr_name.attr.mode = 0444; 114641468a11SChanwoo Choi charger->attr_name.show = charger_name_show; 114741468a11SChanwoo Choi 114841468a11SChanwoo Choi sysfs_attr_init(&charger->attr_state.attr); 114941468a11SChanwoo Choi charger->attr_state.attr.name = "state"; 115041468a11SChanwoo Choi charger->attr_state.attr.mode = 0444; 115141468a11SChanwoo Choi charger->attr_state.show = charger_state_show; 115241468a11SChanwoo Choi 115341468a11SChanwoo Choi sysfs_attr_init(&charger->attr_externally_control.attr); 115441468a11SChanwoo Choi charger->attr_externally_control.attr.name 115541468a11SChanwoo Choi = "externally_control"; 115641468a11SChanwoo Choi charger->attr_externally_control.attr.mode = 0644; 115741468a11SChanwoo Choi charger->attr_externally_control.show 115841468a11SChanwoo Choi = charger_externally_control_show; 115941468a11SChanwoo Choi charger->attr_externally_control.store 116041468a11SChanwoo Choi = charger_externally_control_store; 116141468a11SChanwoo Choi 116241468a11SChanwoo Choi if (!desc->charger_regulators[i].externally_control || 116341468a11SChanwoo Choi !chargers_externally_control) 116441468a11SChanwoo Choi chargers_externally_control = 0; 116541468a11SChanwoo Choi 1166e5409cbdSJoe Perches dev_info(cm->dev, "'%s' regulator's externally_control is %d\n", 1167e5409cbdSJoe Perches charger->regulator_name, charger->externally_control); 116841468a11SChanwoo Choi } 116941468a11SChanwoo Choi 117041468a11SChanwoo Choi if (chargers_externally_control) { 1171e5409cbdSJoe Perches dev_err(cm->dev, "Cannot register regulator because charger-manager must need at least one charger for charging battery\n"); 1172dc6ea7d4SAndi Shyti return -EINVAL; 117341468a11SChanwoo Choi } 117441468a11SChanwoo Choi 1175dc6ea7d4SAndi Shyti return 0; 117641468a11SChanwoo Choi } 117741468a11SChanwoo Choi 1178bdbe8144SKrzysztof Kozlowski static int cm_init_thermal_data(struct charger_manager *cm, 11794cb38258SSebastian Reichel struct power_supply *fuel_gauge, 11804cb38258SSebastian Reichel enum power_supply_property *properties, 11814cb38258SSebastian Reichel size_t *num_properties) 11825c49a625SJonghwa Lee { 11835c49a625SJonghwa Lee struct charger_desc *desc = cm->desc; 11845c49a625SJonghwa Lee union power_supply_propval val; 11855c49a625SJonghwa Lee int ret; 11865c49a625SJonghwa Lee 11875c49a625SJonghwa Lee /* Verify whether fuel gauge provides battery temperature */ 1188b70229bcSKrzysztof Kozlowski ret = power_supply_get_property(fuel_gauge, 11895c49a625SJonghwa Lee POWER_SUPPLY_PROP_TEMP, &val); 11905c49a625SJonghwa Lee 11915c49a625SJonghwa Lee if (!ret) { 11924cb38258SSebastian Reichel properties[*num_properties] = POWER_SUPPLY_PROP_TEMP; 11934cb38258SSebastian Reichel (*num_properties)++; 11945c49a625SJonghwa Lee cm->desc->measure_battery_temp = true; 11955c49a625SJonghwa Lee } 11965c49a625SJonghwa Lee #ifdef CONFIG_THERMAL 11975c49a625SJonghwa Lee if (ret && desc->thermal_zone) { 11985c49a625SJonghwa Lee cm->tzd_batt = 11995c49a625SJonghwa Lee thermal_zone_get_zone_by_name(desc->thermal_zone); 12005c49a625SJonghwa Lee if (IS_ERR(cm->tzd_batt)) 12015c49a625SJonghwa Lee return PTR_ERR(cm->tzd_batt); 12025c49a625SJonghwa Lee 12035c49a625SJonghwa Lee /* Use external thermometer */ 1204cdaeb151SJonathan Bakker properties[*num_properties] = POWER_SUPPLY_PROP_TEMP; 12054cb38258SSebastian Reichel (*num_properties)++; 12065c49a625SJonghwa Lee cm->desc->measure_battery_temp = true; 12075c49a625SJonghwa Lee ret = 0; 12085c49a625SJonghwa Lee } 12095c49a625SJonghwa Lee #endif 12105c49a625SJonghwa Lee if (cm->desc->measure_battery_temp) { 12115c49a625SJonghwa Lee /* NOTICE : Default allowable minimum charge temperature is 0 */ 12125c49a625SJonghwa Lee if (!desc->temp_max) 12135c49a625SJonghwa Lee desc->temp_max = CM_DEFAULT_CHARGE_TEMP_MAX; 12145c49a625SJonghwa Lee if (!desc->temp_diff) 12155c49a625SJonghwa Lee desc->temp_diff = CM_DEFAULT_RECHARGE_TEMP_DIFF; 12165c49a625SJonghwa Lee } 12175c49a625SJonghwa Lee 12185c49a625SJonghwa Lee return ret; 12195c49a625SJonghwa Lee } 12205c49a625SJonghwa Lee 12218fb08855SFabian Frederick static const struct of_device_id charger_manager_match[] = { 1222856ee611SJonghwa Lee { 1223856ee611SJonghwa Lee .compatible = "charger-manager", 1224856ee611SJonghwa Lee }, 1225856ee611SJonghwa Lee {}, 1226856ee611SJonghwa Lee }; 1227856ee611SJonghwa Lee 1228434a09f9SAnton Vorontsov static struct charger_desc *of_cm_parse_desc(struct device *dev) 1229856ee611SJonghwa Lee { 1230856ee611SJonghwa Lee struct charger_desc *desc; 1231856ee611SJonghwa Lee struct device_node *np = dev->of_node; 1232856ee611SJonghwa Lee u32 poll_mode = CM_POLL_DISABLE; 1233856ee611SJonghwa Lee u32 battery_stat = CM_NO_BATTERY; 1234856ee611SJonghwa Lee int num_chgs = 0; 1235856ee611SJonghwa Lee 1236856ee611SJonghwa Lee desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL); 1237856ee611SJonghwa Lee if (!desc) 1238856ee611SJonghwa Lee return ERR_PTR(-ENOMEM); 1239856ee611SJonghwa Lee 1240856ee611SJonghwa Lee of_property_read_string(np, "cm-name", &desc->psy_name); 1241856ee611SJonghwa Lee 1242856ee611SJonghwa Lee of_property_read_u32(np, "cm-poll-mode", &poll_mode); 1243856ee611SJonghwa Lee desc->polling_mode = poll_mode; 1244856ee611SJonghwa Lee 1245856ee611SJonghwa Lee of_property_read_u32(np, "cm-poll-interval", 1246856ee611SJonghwa Lee &desc->polling_interval_ms); 1247856ee611SJonghwa Lee 1248856ee611SJonghwa Lee of_property_read_u32(np, "cm-fullbatt-vchkdrop-volt", 1249856ee611SJonghwa Lee &desc->fullbatt_vchkdrop_uV); 1250856ee611SJonghwa Lee of_property_read_u32(np, "cm-fullbatt-voltage", &desc->fullbatt_uV); 1251856ee611SJonghwa Lee of_property_read_u32(np, "cm-fullbatt-soc", &desc->fullbatt_soc); 1252856ee611SJonghwa Lee of_property_read_u32(np, "cm-fullbatt-capacity", 1253856ee611SJonghwa Lee &desc->fullbatt_full_capacity); 1254856ee611SJonghwa Lee 1255856ee611SJonghwa Lee of_property_read_u32(np, "cm-battery-stat", &battery_stat); 1256856ee611SJonghwa Lee desc->battery_present = battery_stat; 1257856ee611SJonghwa Lee 1258856ee611SJonghwa Lee /* chargers */ 1259856ee611SJonghwa Lee of_property_read_u32(np, "cm-num-chargers", &num_chgs); 1260856ee611SJonghwa Lee if (num_chgs) { 1261a53a68ceSBaolin Wang int i; 1262a53a68ceSBaolin Wang 1263856ee611SJonghwa Lee /* Allocate empty bin at the tail of array */ 1264a86854d0SKees Cook desc->psy_charger_stat = devm_kcalloc(dev, 1265a86854d0SKees Cook num_chgs + 1, 1266a86854d0SKees Cook sizeof(char *), 1267a86854d0SKees Cook GFP_KERNEL); 1268a53a68ceSBaolin Wang if (!desc->psy_charger_stat) 1269a53a68ceSBaolin Wang return ERR_PTR(-ENOMEM); 1270a53a68ceSBaolin Wang 1271856ee611SJonghwa Lee for (i = 0; i < num_chgs; i++) 1272856ee611SJonghwa Lee of_property_read_string_index(np, "cm-chargers", 1273856ee611SJonghwa Lee i, &desc->psy_charger_stat[i]); 1274856ee611SJonghwa Lee } 1275856ee611SJonghwa Lee 1276856ee611SJonghwa Lee of_property_read_string(np, "cm-fuel-gauge", &desc->psy_fuel_gauge); 1277856ee611SJonghwa Lee 1278856ee611SJonghwa Lee of_property_read_string(np, "cm-thermal-zone", &desc->thermal_zone); 1279856ee611SJonghwa Lee 1280856ee611SJonghwa Lee of_property_read_u32(np, "cm-battery-cold", &desc->temp_min); 1281856ee611SJonghwa Lee if (of_get_property(np, "cm-battery-cold-in-minus", NULL)) 1282856ee611SJonghwa Lee desc->temp_min *= -1; 1283856ee611SJonghwa Lee of_property_read_u32(np, "cm-battery-hot", &desc->temp_max); 1284856ee611SJonghwa Lee of_property_read_u32(np, "cm-battery-temp-diff", &desc->temp_diff); 1285856ee611SJonghwa Lee 1286856ee611SJonghwa Lee of_property_read_u32(np, "cm-charging-max", 1287856ee611SJonghwa Lee &desc->charging_max_duration_ms); 1288856ee611SJonghwa Lee of_property_read_u32(np, "cm-discharging-max", 1289856ee611SJonghwa Lee &desc->discharging_max_duration_ms); 1290856ee611SJonghwa Lee 129102276af2SKrzysztof Kozlowski /* battery charger regulators */ 1292856ee611SJonghwa Lee desc->num_charger_regulators = of_get_child_count(np); 1293856ee611SJonghwa Lee if (desc->num_charger_regulators) { 1294856ee611SJonghwa Lee struct charger_regulator *chg_regs; 1295856ee611SJonghwa Lee struct device_node *child; 1296856ee611SJonghwa Lee 1297a86854d0SKees Cook chg_regs = devm_kcalloc(dev, 1298a86854d0SKees Cook desc->num_charger_regulators, 1299a86854d0SKees Cook sizeof(*chg_regs), 1300856ee611SJonghwa Lee GFP_KERNEL); 1301856ee611SJonghwa Lee if (!chg_regs) 1302856ee611SJonghwa Lee return ERR_PTR(-ENOMEM); 1303856ee611SJonghwa Lee 1304856ee611SJonghwa Lee desc->charger_regulators = chg_regs; 1305856ee611SJonghwa Lee 1306157ba1bbSSebastian Reichel desc->sysfs_groups = devm_kcalloc(dev, 1307157ba1bbSSebastian Reichel desc->num_charger_regulators + 1, 1308157ba1bbSSebastian Reichel sizeof(*desc->sysfs_groups), 1309157ba1bbSSebastian Reichel GFP_KERNEL); 1310157ba1bbSSebastian Reichel if (!desc->sysfs_groups) 1311157ba1bbSSebastian Reichel return ERR_PTR(-ENOMEM); 1312157ba1bbSSebastian Reichel 1313856ee611SJonghwa Lee for_each_child_of_node(np, child) { 1314856ee611SJonghwa Lee struct charger_cable *cables; 1315856ee611SJonghwa Lee struct device_node *_child; 1316856ee611SJonghwa Lee 1317856ee611SJonghwa Lee of_property_read_string(child, "cm-regulator-name", 1318856ee611SJonghwa Lee &chg_regs->regulator_name); 1319856ee611SJonghwa Lee 1320856ee611SJonghwa Lee /* charger cables */ 1321856ee611SJonghwa Lee chg_regs->num_cables = of_get_child_count(child); 1322856ee611SJonghwa Lee if (chg_regs->num_cables) { 1323a86854d0SKees Cook cables = devm_kcalloc(dev, 1324a86854d0SKees Cook chg_regs->num_cables, 1325a86854d0SKees Cook sizeof(*cables), 1326856ee611SJonghwa Lee GFP_KERNEL); 13278e5cfb74SJulia Lawall if (!cables) { 13288e5cfb74SJulia Lawall of_node_put(child); 1329856ee611SJonghwa Lee return ERR_PTR(-ENOMEM); 13308e5cfb74SJulia Lawall } 1331856ee611SJonghwa Lee 1332856ee611SJonghwa Lee chg_regs->cables = cables; 1333856ee611SJonghwa Lee 1334856ee611SJonghwa Lee for_each_child_of_node(child, _child) { 1335856ee611SJonghwa Lee of_property_read_string(_child, 1336856ee611SJonghwa Lee "cm-cable-name", &cables->name); 1337856ee611SJonghwa Lee of_property_read_string(_child, 1338856ee611SJonghwa Lee "cm-cable-extcon", 1339856ee611SJonghwa Lee &cables->extcon_name); 1340856ee611SJonghwa Lee of_property_read_u32(_child, 1341856ee611SJonghwa Lee "cm-cable-min", 1342856ee611SJonghwa Lee &cables->min_uA); 1343856ee611SJonghwa Lee of_property_read_u32(_child, 1344856ee611SJonghwa Lee "cm-cable-max", 1345856ee611SJonghwa Lee &cables->max_uA); 1346856ee611SJonghwa Lee cables++; 1347856ee611SJonghwa Lee } 1348856ee611SJonghwa Lee } 1349856ee611SJonghwa Lee chg_regs++; 1350856ee611SJonghwa Lee } 1351856ee611SJonghwa Lee } 1352856ee611SJonghwa Lee return desc; 1353856ee611SJonghwa Lee } 1354856ee611SJonghwa Lee 1355856ee611SJonghwa Lee static inline struct charger_desc *cm_get_drv_data(struct platform_device *pdev) 1356856ee611SJonghwa Lee { 1357856ee611SJonghwa Lee if (pdev->dev.of_node) 1358856ee611SJonghwa Lee return of_cm_parse_desc(&pdev->dev); 135986515b7dSJingoo Han return dev_get_platdata(&pdev->dev); 1360856ee611SJonghwa Lee } 1361856ee611SJonghwa Lee 1362c1155c64SJonghwa Lee static enum alarmtimer_restart cm_timer_func(struct alarm *alarm, ktime_t now) 1363c1155c64SJonghwa Lee { 1364c1155c64SJonghwa Lee cm_timer_set = false; 1365c1155c64SJonghwa Lee return ALARMTIMER_NORESTART; 1366c1155c64SJonghwa Lee } 1367c1155c64SJonghwa Lee 13683bb3dbbdSDonggeun Kim static int charger_manager_probe(struct platform_device *pdev) 13693bb3dbbdSDonggeun Kim { 1370856ee611SJonghwa Lee struct charger_desc *desc = cm_get_drv_data(pdev); 13713bb3dbbdSDonggeun Kim struct charger_manager *cm; 1372dc6ea7d4SAndi Shyti int ret, i = 0; 1373bee737bcSChanwoo Choi int j = 0; 1374ad3d13eeSDonggeun Kim union power_supply_propval val; 1375bdbe8144SKrzysztof Kozlowski struct power_supply *fuel_gauge; 13764cb38258SSebastian Reichel enum power_supply_property *properties; 13774cb38258SSebastian Reichel size_t num_properties; 1378297d716fSKrzysztof Kozlowski struct power_supply_config psy_cfg = {}; 13793bb3dbbdSDonggeun Kim 1380c6738d06SChanwoo Choi if (IS_ERR(desc)) { 1381e5409cbdSJoe Perches dev_err(&pdev->dev, "No platform data (desc) found\n"); 1382f25a646fSBaolin Wang return PTR_ERR(desc); 13833bb3dbbdSDonggeun Kim } 13843bb3dbbdSDonggeun Kim 138518a89d5cSChristophe JAILLET cm = devm_kzalloc(&pdev->dev, sizeof(*cm), GFP_KERNEL); 1386883c10a9SJonghwa Lee if (!cm) 1387883c10a9SJonghwa Lee return -ENOMEM; 13883bb3dbbdSDonggeun Kim 13893bb3dbbdSDonggeun Kim /* Basic Values. Unspecified are Null or 0 */ 13903bb3dbbdSDonggeun Kim cm->dev = &pdev->dev; 1391883c10a9SJonghwa Lee cm->desc = desc; 1392297d716fSKrzysztof Kozlowski psy_cfg.drv_data = cm; 13933bb3dbbdSDonggeun Kim 1394c1155c64SJonghwa Lee /* Initialize alarm timer */ 1395c1155c64SJonghwa Lee if (alarmtimer_get_rtcdev()) { 1396c1155c64SJonghwa Lee cm_timer = devm_kzalloc(cm->dev, sizeof(*cm_timer), GFP_KERNEL); 1397096fc160SChristophe JAILLET if (!cm_timer) 1398096fc160SChristophe JAILLET return -ENOMEM; 1399c1155c64SJonghwa Lee alarm_init(cm_timer, ALARM_BOOTTIME, cm_timer_func); 1400c1155c64SJonghwa Lee } 1401c1155c64SJonghwa Lee 1402d829dc75SChanwoo Choi /* 14030299484eSChristophe JAILLET * Some of the following do not need to be errors. 14040299484eSChristophe JAILLET * Users may intentionally ignore those features. 1405d829dc75SChanwoo Choi */ 1406d829dc75SChanwoo Choi if (desc->fullbatt_uV == 0) { 1407e5409cbdSJoe Perches dev_info(&pdev->dev, "Ignoring full-battery voltage threshold as it is not supplied\n"); 1408d829dc75SChanwoo Choi } 14099584051fSJonghwa Lee if (!desc->fullbatt_vchkdrop_uV) { 1410e5409cbdSJoe Perches dev_info(&pdev->dev, "Disabling full-battery voltage drop checking mechanism as it is not supplied\n"); 1411d829dc75SChanwoo Choi desc->fullbatt_vchkdrop_uV = 0; 1412d829dc75SChanwoo Choi } 14132ed9e9b6SChanwoo Choi if (desc->fullbatt_soc == 0) { 1414e5409cbdSJoe Perches dev_info(&pdev->dev, "Ignoring full-battery soc(state of charge) threshold as it is not supplied\n"); 14152ed9e9b6SChanwoo Choi } 14162ed9e9b6SChanwoo Choi if (desc->fullbatt_full_capacity == 0) { 1417e5409cbdSJoe Perches dev_info(&pdev->dev, "Ignoring full-battery full capacity threshold as it is not supplied\n"); 14182ed9e9b6SChanwoo Choi } 1419d829dc75SChanwoo Choi 14203bb3dbbdSDonggeun Kim if (!desc->charger_regulators || desc->num_charger_regulators < 1) { 1421e5409cbdSJoe Perches dev_err(&pdev->dev, "charger_regulators undefined\n"); 1422883c10a9SJonghwa Lee return -EINVAL; 14233bb3dbbdSDonggeun Kim } 14243bb3dbbdSDonggeun Kim 14253bb3dbbdSDonggeun Kim if (!desc->psy_charger_stat || !desc->psy_charger_stat[0]) { 1426e5409cbdSJoe Perches dev_err(&pdev->dev, "No power supply defined\n"); 1427883c10a9SJonghwa Lee return -EINVAL; 14283bb3dbbdSDonggeun Kim } 14293bb3dbbdSDonggeun Kim 1430661a8886SKrzysztof Kozlowski if (!desc->psy_fuel_gauge) { 1431661a8886SKrzysztof Kozlowski dev_err(&pdev->dev, "No fuel gauge power supply defined\n"); 1432661a8886SKrzysztof Kozlowski return -EINVAL; 1433661a8886SKrzysztof Kozlowski } 1434661a8886SKrzysztof Kozlowski 1435cdaf3e15SKrzysztof Kozlowski /* Check if charger's supplies are present at probe */ 14363bb3dbbdSDonggeun Kim for (i = 0; desc->psy_charger_stat[i]; i++) { 1437cdaf3e15SKrzysztof Kozlowski struct power_supply *psy; 1438cdaf3e15SKrzysztof Kozlowski 1439cdaf3e15SKrzysztof Kozlowski psy = power_supply_get_by_name(desc->psy_charger_stat[i]); 1440cdaf3e15SKrzysztof Kozlowski if (!psy) { 1441e5409cbdSJoe Perches dev_err(&pdev->dev, "Cannot find power supply \"%s\"\n", 14423bb3dbbdSDonggeun Kim desc->psy_charger_stat[i]); 1443883c10a9SJonghwa Lee return -ENODEV; 14443bb3dbbdSDonggeun Kim } 1445b43eb35aSKrzysztof Kozlowski power_supply_put(psy); 14463bb3dbbdSDonggeun Kim } 14473bb3dbbdSDonggeun Kim 1448111242d6SLadislav Michl if (cm->desc->polling_mode != CM_POLL_DISABLE && 1449111242d6SLadislav Michl (desc->polling_interval_ms == 0 || 1450111242d6SLadislav Michl msecs_to_jiffies(desc->polling_interval_ms) <= CM_JIFFIES_SMALL)) { 14513bb3dbbdSDonggeun Kim dev_err(&pdev->dev, "polling_interval_ms is too small\n"); 1452883c10a9SJonghwa Lee return -EINVAL; 14533bb3dbbdSDonggeun Kim } 14543bb3dbbdSDonggeun Kim 14558fcfe088SChanwoo Choi if (!desc->charging_max_duration_ms || 14568fcfe088SChanwoo Choi !desc->discharging_max_duration_ms) { 1457e5409cbdSJoe Perches dev_info(&pdev->dev, "Cannot limit charging duration checking mechanism to prevent overcharge/overheat and control discharging duration\n"); 14588fcfe088SChanwoo Choi desc->charging_max_duration_ms = 0; 14598fcfe088SChanwoo Choi desc->discharging_max_duration_ms = 0; 14608fcfe088SChanwoo Choi } 14618fcfe088SChanwoo Choi 14623bb3dbbdSDonggeun Kim platform_set_drvdata(pdev, cm); 14633bb3dbbdSDonggeun Kim 1464297d716fSKrzysztof Kozlowski memcpy(&cm->charger_psy_desc, &psy_default, sizeof(psy_default)); 1465bb2a95c2SAxel Lin 146641468a11SChanwoo Choi if (!desc->psy_name) 1467bb2a95c2SAxel Lin strncpy(cm->psy_name_buf, psy_default.name, PSY_NAME_MAX); 146841468a11SChanwoo Choi else 1469ad3d13eeSDonggeun Kim strncpy(cm->psy_name_buf, desc->psy_name, PSY_NAME_MAX); 1470297d716fSKrzysztof Kozlowski cm->charger_psy_desc.name = cm->psy_name_buf; 1471ad3d13eeSDonggeun Kim 1472ad3d13eeSDonggeun Kim /* Allocate for psy properties because they may vary */ 14734cb38258SSebastian Reichel properties = devm_kcalloc(&pdev->dev, 1474a86854d0SKees Cook ARRAY_SIZE(default_charger_props) + 1475a86854d0SKees Cook NUM_CHARGER_PSY_OPTIONAL, 14764cb38258SSebastian Reichel sizeof(*properties), GFP_KERNEL); 14774cb38258SSebastian Reichel if (!properties) 1478883c10a9SJonghwa Lee return -ENOMEM; 1479883c10a9SJonghwa Lee 14804cb38258SSebastian Reichel memcpy(properties, default_charger_props, 1481ad3d13eeSDonggeun Kim sizeof(enum power_supply_property) * 1482ad3d13eeSDonggeun Kim ARRAY_SIZE(default_charger_props)); 148397ed79f4SMichał Mirosław num_properties = ARRAY_SIZE(default_charger_props); 1484ad3d13eeSDonggeun Kim 1485ad3d13eeSDonggeun Kim /* Find which optional psy-properties are available */ 1486b43eb35aSKrzysztof Kozlowski fuel_gauge = power_supply_get_by_name(desc->psy_fuel_gauge); 1487b43eb35aSKrzysztof Kozlowski if (!fuel_gauge) { 1488b43eb35aSKrzysztof Kozlowski dev_err(&pdev->dev, "Cannot find power supply \"%s\"\n", 1489b43eb35aSKrzysztof Kozlowski desc->psy_fuel_gauge); 1490b43eb35aSKrzysztof Kozlowski return -ENODEV; 1491b43eb35aSKrzysztof Kozlowski } 1492b70229bcSKrzysztof Kozlowski if (!power_supply_get_property(fuel_gauge, 14930a9e0f94SJonghwa Lee POWER_SUPPLY_PROP_CHARGE_FULL, &val)) { 14940a9e0f94SJonghwa Lee properties[num_properties] = 14950a9e0f94SJonghwa Lee POWER_SUPPLY_PROP_CHARGE_FULL; 14960a9e0f94SJonghwa Lee num_properties++; 14970a9e0f94SJonghwa Lee } 14980a9e0f94SJonghwa Lee if (!power_supply_get_property(fuel_gauge, 1499ad3d13eeSDonggeun Kim POWER_SUPPLY_PROP_CHARGE_NOW, &val)) { 15004cb38258SSebastian Reichel properties[num_properties] = 1501ad3d13eeSDonggeun Kim POWER_SUPPLY_PROP_CHARGE_NOW; 15024cb38258SSebastian Reichel num_properties++; 1503ad3d13eeSDonggeun Kim } 1504b70229bcSKrzysztof Kozlowski if (!power_supply_get_property(fuel_gauge, 1505ad3d13eeSDonggeun Kim POWER_SUPPLY_PROP_CURRENT_NOW, 1506ad3d13eeSDonggeun Kim &val)) { 15074cb38258SSebastian Reichel properties[num_properties] = 1508ad3d13eeSDonggeun Kim POWER_SUPPLY_PROP_CURRENT_NOW; 15094cb38258SSebastian Reichel num_properties++; 1510ad3d13eeSDonggeun Kim } 1511bb2a95c2SAxel Lin 15124cb38258SSebastian Reichel ret = cm_init_thermal_data(cm, fuel_gauge, properties, &num_properties); 15135c49a625SJonghwa Lee if (ret) { 15145c49a625SJonghwa Lee dev_err(&pdev->dev, "Failed to initialize thermal data\n"); 15155c49a625SJonghwa Lee cm->desc->measure_battery_temp = false; 1516ad3d13eeSDonggeun Kim } 1517b43eb35aSKrzysztof Kozlowski power_supply_put(fuel_gauge); 1518ad3d13eeSDonggeun Kim 15194cb38258SSebastian Reichel cm->charger_psy_desc.properties = properties; 15204cb38258SSebastian Reichel cm->charger_psy_desc.num_properties = num_properties; 15214cb38258SSebastian Reichel 1522157ba1bbSSebastian Reichel /* Register sysfs entry for charger(regulator) */ 1523157ba1bbSSebastian Reichel ret = charger_manager_prepare_sysfs(cm); 1524157ba1bbSSebastian Reichel if (ret < 0) { 1525157ba1bbSSebastian Reichel dev_err(&pdev->dev, 1526157ba1bbSSebastian Reichel "Cannot prepare sysfs entry of regulators\n"); 1527157ba1bbSSebastian Reichel return ret; 1528157ba1bbSSebastian Reichel } 1529157ba1bbSSebastian Reichel psy_cfg.attr_grp = desc->sysfs_groups; 1530157ba1bbSSebastian Reichel 15314970d839SKrzysztof Kozlowski cm->charger_psy = power_supply_register(&pdev->dev, 15324970d839SKrzysztof Kozlowski &cm->charger_psy_desc, 1533297d716fSKrzysztof Kozlowski &psy_cfg); 1534297d716fSKrzysztof Kozlowski if (IS_ERR(cm->charger_psy)) { 1535e5409cbdSJoe Perches dev_err(&pdev->dev, "Cannot register charger-manager with name \"%s\"\n", 1536ecf896b9SKrzysztof Kozlowski cm->charger_psy_desc.name); 1537297d716fSKrzysztof Kozlowski return PTR_ERR(cm->charger_psy); 1538ad3d13eeSDonggeun Kim } 1539ad3d13eeSDonggeun Kim 154041468a11SChanwoo Choi /* Register extcon device for charger cable */ 154141468a11SChanwoo Choi ret = charger_manager_register_extcon(cm); 1542bee737bcSChanwoo Choi if (ret < 0) { 154341468a11SChanwoo Choi dev_err(&pdev->dev, "Cannot initialize extcon device\n"); 154441468a11SChanwoo Choi goto err_reg_extcon; 1545bee737bcSChanwoo Choi } 15463950c786SChanwoo Choi 15473bb3dbbdSDonggeun Kim /* Add to the list */ 15483bb3dbbdSDonggeun Kim mutex_lock(&cm_list_mtx); 15493bb3dbbdSDonggeun Kim list_add(&cm->entry, &cm_list); 15503bb3dbbdSDonggeun Kim mutex_unlock(&cm_list_mtx); 15513bb3dbbdSDonggeun Kim 1552dfeccb12SChanwoo Choi /* 1553dfeccb12SChanwoo Choi * Charger-manager is capable of waking up the systme from sleep 15548c13b6f1SBaolin Wang * when event is happened through cm_notify_event() 1555dfeccb12SChanwoo Choi */ 1556dfeccb12SChanwoo Choi device_init_wakeup(&pdev->dev, true); 1557dfeccb12SChanwoo Choi device_set_wakeup_capable(&pdev->dev, false); 1558dfeccb12SChanwoo Choi 1559b1022e24SChanwoo Choi /* 1560b1022e24SChanwoo Choi * Charger-manager have to check the charging state right after 156102276af2SKrzysztof Kozlowski * initialization of charger-manager and then update current charging 1562b1022e24SChanwoo Choi * state. 1563b1022e24SChanwoo Choi */ 1564b1022e24SChanwoo Choi cm_monitor(); 1565b1022e24SChanwoo Choi 1566d829dc75SChanwoo Choi schedule_work(&setup_polling); 1567d829dc75SChanwoo Choi 15683bb3dbbdSDonggeun Kim return 0; 15693bb3dbbdSDonggeun Kim 157041468a11SChanwoo Choi err_reg_extcon: 1571bee737bcSChanwoo Choi for (i = 0; i < desc->num_charger_regulators; i++) { 157241468a11SChanwoo Choi struct charger_regulator *charger; 157341468a11SChanwoo Choi 157441468a11SChanwoo Choi charger = &desc->charger_regulators[i]; 1575bee737bcSChanwoo Choi for (j = 0; j < charger->num_cables; j++) { 1576bee737bcSChanwoo Choi struct charger_cable *cable = &charger->cables[j]; 15773cc9d269SJonghwa Lee /* Remove notifier block if only edev exists */ 15783cc9d269SJonghwa Lee if (cable->extcon_dev.edev) 1579bee737bcSChanwoo Choi extcon_unregister_interest(&cable->extcon_dev); 1580bee737bcSChanwoo Choi } 158141468a11SChanwoo Choi 1582bee737bcSChanwoo Choi regulator_put(desc->charger_regulators[i].consumer); 158341468a11SChanwoo Choi } 1584bee737bcSChanwoo Choi 1585297d716fSKrzysztof Kozlowski power_supply_unregister(cm->charger_psy); 1586883c10a9SJonghwa Lee 15873bb3dbbdSDonggeun Kim return ret; 15883bb3dbbdSDonggeun Kim } 15893bb3dbbdSDonggeun Kim 1590415ec69fSBill Pemberton static int charger_manager_remove(struct platform_device *pdev) 15913bb3dbbdSDonggeun Kim { 15923bb3dbbdSDonggeun Kim struct charger_manager *cm = platform_get_drvdata(pdev); 15933bb3dbbdSDonggeun Kim struct charger_desc *desc = cm->desc; 1594bee737bcSChanwoo Choi int i = 0; 1595bee737bcSChanwoo Choi int j = 0; 15963bb3dbbdSDonggeun Kim 15973bb3dbbdSDonggeun Kim /* Remove from the list */ 15983bb3dbbdSDonggeun Kim mutex_lock(&cm_list_mtx); 15993bb3dbbdSDonggeun Kim list_del(&cm->entry); 16003bb3dbbdSDonggeun Kim mutex_unlock(&cm_list_mtx); 16013bb3dbbdSDonggeun Kim 1602d829dc75SChanwoo Choi cancel_work_sync(&setup_polling); 1603d829dc75SChanwoo Choi cancel_delayed_work_sync(&cm_monitor_work); 1604d829dc75SChanwoo Choi 1605bee737bcSChanwoo Choi for (i = 0 ; i < desc->num_charger_regulators ; i++) { 1606bee737bcSChanwoo Choi struct charger_regulator *charger 1607bee737bcSChanwoo Choi = &desc->charger_regulators[i]; 1608bee737bcSChanwoo Choi for (j = 0 ; j < charger->num_cables ; j++) { 1609bee737bcSChanwoo Choi struct charger_cable *cable = &charger->cables[j]; 1610bee737bcSChanwoo Choi extcon_unregister_interest(&cable->extcon_dev); 1611bee737bcSChanwoo Choi } 1612bee737bcSChanwoo Choi } 1613bee737bcSChanwoo Choi 1614bee737bcSChanwoo Choi for (i = 0 ; i < desc->num_charger_regulators ; i++) 1615bee737bcSChanwoo Choi regulator_put(desc->charger_regulators[i].consumer); 1616bee737bcSChanwoo Choi 1617297d716fSKrzysztof Kozlowski power_supply_unregister(cm->charger_psy); 1618d829dc75SChanwoo Choi 1619d829dc75SChanwoo Choi try_charger_enable(cm, false); 1620d829dc75SChanwoo Choi 16213bb3dbbdSDonggeun Kim return 0; 16223bb3dbbdSDonggeun Kim } 16233bb3dbbdSDonggeun Kim 16241bbe24d4SAxel Lin static const struct platform_device_id charger_manager_id[] = { 16253bb3dbbdSDonggeun Kim { "charger-manager", 0 }, 16263bb3dbbdSDonggeun Kim { }, 16273bb3dbbdSDonggeun Kim }; 16281bbe24d4SAxel Lin MODULE_DEVICE_TABLE(platform, charger_manager_id); 16293bb3dbbdSDonggeun Kim 1630dfeccb12SChanwoo Choi static int cm_suspend_noirq(struct device *dev) 1631dfeccb12SChanwoo Choi { 1632dfeccb12SChanwoo Choi if (device_may_wakeup(dev)) { 1633dfeccb12SChanwoo Choi device_set_wakeup_capable(dev, false); 1634dc6ea7d4SAndi Shyti return -EAGAIN; 1635dfeccb12SChanwoo Choi } 1636dfeccb12SChanwoo Choi 1637dc6ea7d4SAndi Shyti return 0; 1638dfeccb12SChanwoo Choi } 1639dfeccb12SChanwoo Choi 1640c1155c64SJonghwa Lee static bool cm_need_to_awake(void) 1641c1155c64SJonghwa Lee { 1642c1155c64SJonghwa Lee struct charger_manager *cm; 1643c1155c64SJonghwa Lee 1644c1155c64SJonghwa Lee if (cm_timer) 1645c1155c64SJonghwa Lee return false; 1646c1155c64SJonghwa Lee 1647c1155c64SJonghwa Lee mutex_lock(&cm_list_mtx); 1648c1155c64SJonghwa Lee list_for_each_entry(cm, &cm_list, entry) { 1649c1155c64SJonghwa Lee if (is_charging(cm)) { 1650c1155c64SJonghwa Lee mutex_unlock(&cm_list_mtx); 1651c1155c64SJonghwa Lee return true; 1652c1155c64SJonghwa Lee } 1653c1155c64SJonghwa Lee } 1654c1155c64SJonghwa Lee mutex_unlock(&cm_list_mtx); 1655c1155c64SJonghwa Lee 1656c1155c64SJonghwa Lee return false; 1657c1155c64SJonghwa Lee } 1658c1155c64SJonghwa Lee 16593bb3dbbdSDonggeun Kim static int cm_suspend_prepare(struct device *dev) 16603bb3dbbdSDonggeun Kim { 1661c1155c64SJonghwa Lee if (cm_need_to_awake()) 1662c1155c64SJonghwa Lee return -EBUSY; 16633bb3dbbdSDonggeun Kim 1664c1155c64SJonghwa Lee if (!cm_suspended) 16653bb3dbbdSDonggeun Kim cm_suspended = true; 16663bb3dbbdSDonggeun Kim 1667c1155c64SJonghwa Lee cm_timer_set = cm_setup_timer(); 1668c1155c64SJonghwa Lee 1669c1155c64SJonghwa Lee if (cm_timer_set) { 1670c1155c64SJonghwa Lee cancel_work_sync(&setup_polling); 1671c1155c64SJonghwa Lee cancel_delayed_work_sync(&cm_monitor_work); 16723bb3dbbdSDonggeun Kim } 16733bb3dbbdSDonggeun Kim 16743bb3dbbdSDonggeun Kim return 0; 16753bb3dbbdSDonggeun Kim } 16763bb3dbbdSDonggeun Kim 16773bb3dbbdSDonggeun Kim static void cm_suspend_complete(struct device *dev) 16783bb3dbbdSDonggeun Kim { 1679bb2a95c2SAxel Lin struct charger_manager *cm = dev_get_drvdata(dev); 16803bb3dbbdSDonggeun Kim 1681c1155c64SJonghwa Lee if (cm_suspended) 16823bb3dbbdSDonggeun Kim cm_suspended = false; 1683c1155c64SJonghwa Lee 1684c1155c64SJonghwa Lee if (cm_timer_set) { 1685c1155c64SJonghwa Lee ktime_t remain; 1686c1155c64SJonghwa Lee 1687c1155c64SJonghwa Lee alarm_cancel(cm_timer); 1688c1155c64SJonghwa Lee cm_timer_set = false; 1689c1155c64SJonghwa Lee remain = alarm_expires_remaining(cm_timer); 1690c1155c64SJonghwa Lee cm_suspend_duration_ms -= ktime_to_ms(remain); 1691c1155c64SJonghwa Lee schedule_work(&setup_polling); 16923bb3dbbdSDonggeun Kim } 16933bb3dbbdSDonggeun Kim 1694c1155c64SJonghwa Lee _cm_monitor(cm); 1695c1155c64SJonghwa Lee 1696dfeccb12SChanwoo Choi device_set_wakeup_capable(cm->dev, false); 16973bb3dbbdSDonggeun Kim } 16983bb3dbbdSDonggeun Kim 16993bb3dbbdSDonggeun Kim static const struct dev_pm_ops charger_manager_pm = { 17003bb3dbbdSDonggeun Kim .prepare = cm_suspend_prepare, 1701dfeccb12SChanwoo Choi .suspend_noirq = cm_suspend_noirq, 17023bb3dbbdSDonggeun Kim .complete = cm_suspend_complete, 17033bb3dbbdSDonggeun Kim }; 17043bb3dbbdSDonggeun Kim 17053bb3dbbdSDonggeun Kim static struct platform_driver charger_manager_driver = { 17063bb3dbbdSDonggeun Kim .driver = { 17073bb3dbbdSDonggeun Kim .name = "charger-manager", 17083bb3dbbdSDonggeun Kim .pm = &charger_manager_pm, 1709856ee611SJonghwa Lee .of_match_table = charger_manager_match, 17103bb3dbbdSDonggeun Kim }, 17113bb3dbbdSDonggeun Kim .probe = charger_manager_probe, 171228ea73f4SBill Pemberton .remove = charger_manager_remove, 17133bb3dbbdSDonggeun Kim .id_table = charger_manager_id, 17143bb3dbbdSDonggeun Kim }; 17153bb3dbbdSDonggeun Kim 17163bb3dbbdSDonggeun Kim static int __init charger_manager_init(void) 17173bb3dbbdSDonggeun Kim { 1718d829dc75SChanwoo Choi cm_wq = create_freezable_workqueue("charger_manager"); 171975cf4f5aSKangjie Lu if (unlikely(!cm_wq)) 172075cf4f5aSKangjie Lu return -ENOMEM; 172175cf4f5aSKangjie Lu 1722d829dc75SChanwoo Choi INIT_DELAYED_WORK(&cm_monitor_work, cm_monitor_poller); 1723d829dc75SChanwoo Choi 17243bb3dbbdSDonggeun Kim return platform_driver_register(&charger_manager_driver); 17253bb3dbbdSDonggeun Kim } 17263bb3dbbdSDonggeun Kim late_initcall(charger_manager_init); 17273bb3dbbdSDonggeun Kim 17283bb3dbbdSDonggeun Kim static void __exit charger_manager_cleanup(void) 17293bb3dbbdSDonggeun Kim { 1730d829dc75SChanwoo Choi destroy_workqueue(cm_wq); 1731d829dc75SChanwoo Choi cm_wq = NULL; 1732d829dc75SChanwoo Choi 17333bb3dbbdSDonggeun Kim platform_driver_unregister(&charger_manager_driver); 17343bb3dbbdSDonggeun Kim } 17353bb3dbbdSDonggeun Kim module_exit(charger_manager_cleanup); 17363bb3dbbdSDonggeun Kim 17373bb3dbbdSDonggeun Kim MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>"); 17383bb3dbbdSDonggeun Kim MODULE_DESCRIPTION("Charger Manager"); 17393bb3dbbdSDonggeun Kim MODULE_LICENSE("GPL"); 1740