162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2011 Samsung Electronics Co., Ltd. 462306a36Sopenharmony_ci * MyungJoo Ham <myungjoo.ham@samsung.com> 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * This driver enables to monitor battery health and control charger 762306a36Sopenharmony_ci * during suspend-to-mem. 862306a36Sopenharmony_ci * Charger manager depends on other devices. Register this later than 962306a36Sopenharmony_ci * the depending devices. 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci**/ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <linux/io.h> 1662306a36Sopenharmony_ci#include <linux/module.h> 1762306a36Sopenharmony_ci#include <linux/irq.h> 1862306a36Sopenharmony_ci#include <linux/interrupt.h> 1962306a36Sopenharmony_ci#include <linux/rtc.h> 2062306a36Sopenharmony_ci#include <linux/slab.h> 2162306a36Sopenharmony_ci#include <linux/workqueue.h> 2262306a36Sopenharmony_ci#include <linux/platform_device.h> 2362306a36Sopenharmony_ci#include <linux/power/charger-manager.h> 2462306a36Sopenharmony_ci#include <linux/regulator/consumer.h> 2562306a36Sopenharmony_ci#include <linux/sysfs.h> 2662306a36Sopenharmony_ci#include <linux/of.h> 2762306a36Sopenharmony_ci#include <linux/thermal.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistatic struct { 3062306a36Sopenharmony_ci const char *name; 3162306a36Sopenharmony_ci u64 extcon_type; 3262306a36Sopenharmony_ci} extcon_mapping[] = { 3362306a36Sopenharmony_ci /* Current textual representations */ 3462306a36Sopenharmony_ci { "USB", EXTCON_USB }, 3562306a36Sopenharmony_ci { "USB-HOST", EXTCON_USB_HOST }, 3662306a36Sopenharmony_ci { "SDP", EXTCON_CHG_USB_SDP }, 3762306a36Sopenharmony_ci { "DCP", EXTCON_CHG_USB_DCP }, 3862306a36Sopenharmony_ci { "CDP", EXTCON_CHG_USB_CDP }, 3962306a36Sopenharmony_ci { "ACA", EXTCON_CHG_USB_ACA }, 4062306a36Sopenharmony_ci { "FAST-CHARGER", EXTCON_CHG_USB_FAST }, 4162306a36Sopenharmony_ci { "SLOW-CHARGER", EXTCON_CHG_USB_SLOW }, 4262306a36Sopenharmony_ci { "WPT", EXTCON_CHG_WPT }, 4362306a36Sopenharmony_ci { "PD", EXTCON_CHG_USB_PD }, 4462306a36Sopenharmony_ci { "DOCK", EXTCON_DOCK }, 4562306a36Sopenharmony_ci { "JIG", EXTCON_JIG }, 4662306a36Sopenharmony_ci { "MECHANICAL", EXTCON_MECHANICAL }, 4762306a36Sopenharmony_ci /* Deprecated textual representations */ 4862306a36Sopenharmony_ci { "TA", EXTCON_CHG_USB_SDP }, 4962306a36Sopenharmony_ci { "CHARGE-DOWNSTREAM", EXTCON_CHG_USB_CDP }, 5062306a36Sopenharmony_ci}; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci/* 5362306a36Sopenharmony_ci * Default temperature threshold for charging. 5462306a36Sopenharmony_ci * Every temperature units are in tenth of centigrade. 5562306a36Sopenharmony_ci */ 5662306a36Sopenharmony_ci#define CM_DEFAULT_RECHARGE_TEMP_DIFF 50 5762306a36Sopenharmony_ci#define CM_DEFAULT_CHARGE_TEMP_MAX 500 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci/* 6062306a36Sopenharmony_ci * Regard CM_JIFFIES_SMALL jiffies is small enough to ignore for 6162306a36Sopenharmony_ci * delayed works so that we can run delayed works with CM_JIFFIES_SMALL 6262306a36Sopenharmony_ci * without any delays. 6362306a36Sopenharmony_ci */ 6462306a36Sopenharmony_ci#define CM_JIFFIES_SMALL (2) 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci/* If y is valid (> 0) and smaller than x, do x = y */ 6762306a36Sopenharmony_ci#define CM_MIN_VALID(x, y) x = (((y > 0) && ((x) > (y))) ? (y) : (x)) 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci/* 7062306a36Sopenharmony_ci * Regard CM_RTC_SMALL (sec) is small enough to ignore error in invoking 7162306a36Sopenharmony_ci * rtc alarm. It should be 2 or larger 7262306a36Sopenharmony_ci */ 7362306a36Sopenharmony_ci#define CM_RTC_SMALL (2) 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic LIST_HEAD(cm_list); 7662306a36Sopenharmony_cistatic DEFINE_MUTEX(cm_list_mtx); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci/* About in-suspend (suspend-again) monitoring */ 7962306a36Sopenharmony_cistatic struct alarm *cm_timer; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic bool cm_suspended; 8262306a36Sopenharmony_cistatic bool cm_timer_set; 8362306a36Sopenharmony_cistatic unsigned long cm_suspend_duration_ms; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci/* About normal (not suspended) monitoring */ 8662306a36Sopenharmony_cistatic unsigned long polling_jiffy = ULONG_MAX; /* ULONG_MAX: no polling */ 8762306a36Sopenharmony_cistatic unsigned long next_polling; /* Next appointed polling time */ 8862306a36Sopenharmony_cistatic struct workqueue_struct *cm_wq; /* init at driver add */ 8962306a36Sopenharmony_cistatic struct delayed_work cm_monitor_work; /* init at driver add */ 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci/** 9262306a36Sopenharmony_ci * is_batt_present - See if the battery presents in place. 9362306a36Sopenharmony_ci * @cm: the Charger Manager representing the battery. 9462306a36Sopenharmony_ci */ 9562306a36Sopenharmony_cistatic bool is_batt_present(struct charger_manager *cm) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci union power_supply_propval val; 9862306a36Sopenharmony_ci struct power_supply *psy; 9962306a36Sopenharmony_ci bool present = false; 10062306a36Sopenharmony_ci int i, ret; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci switch (cm->desc->battery_present) { 10362306a36Sopenharmony_ci case CM_BATTERY_PRESENT: 10462306a36Sopenharmony_ci present = true; 10562306a36Sopenharmony_ci break; 10662306a36Sopenharmony_ci case CM_NO_BATTERY: 10762306a36Sopenharmony_ci break; 10862306a36Sopenharmony_ci case CM_FUEL_GAUGE: 10962306a36Sopenharmony_ci psy = power_supply_get_by_name(cm->desc->psy_fuel_gauge); 11062306a36Sopenharmony_ci if (!psy) 11162306a36Sopenharmony_ci break; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_PRESENT, 11462306a36Sopenharmony_ci &val); 11562306a36Sopenharmony_ci if (ret == 0 && val.intval) 11662306a36Sopenharmony_ci present = true; 11762306a36Sopenharmony_ci power_supply_put(psy); 11862306a36Sopenharmony_ci break; 11962306a36Sopenharmony_ci case CM_CHARGER_STAT: 12062306a36Sopenharmony_ci for (i = 0; cm->desc->psy_charger_stat[i]; i++) { 12162306a36Sopenharmony_ci psy = power_supply_get_by_name( 12262306a36Sopenharmony_ci cm->desc->psy_charger_stat[i]); 12362306a36Sopenharmony_ci if (!psy) { 12462306a36Sopenharmony_ci dev_err(cm->dev, "Cannot find power supply \"%s\"\n", 12562306a36Sopenharmony_ci cm->desc->psy_charger_stat[i]); 12662306a36Sopenharmony_ci continue; 12762306a36Sopenharmony_ci } 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci ret = power_supply_get_property(psy, 13062306a36Sopenharmony_ci POWER_SUPPLY_PROP_PRESENT, &val); 13162306a36Sopenharmony_ci power_supply_put(psy); 13262306a36Sopenharmony_ci if (ret == 0 && val.intval) { 13362306a36Sopenharmony_ci present = true; 13462306a36Sopenharmony_ci break; 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci break; 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci return present; 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci/** 14462306a36Sopenharmony_ci * is_ext_pwr_online - See if an external power source is attached to charge 14562306a36Sopenharmony_ci * @cm: the Charger Manager representing the battery. 14662306a36Sopenharmony_ci * 14762306a36Sopenharmony_ci * Returns true if at least one of the chargers of the battery has an external 14862306a36Sopenharmony_ci * power source attached to charge the battery regardless of whether it is 14962306a36Sopenharmony_ci * actually charging or not. 15062306a36Sopenharmony_ci */ 15162306a36Sopenharmony_cistatic bool is_ext_pwr_online(struct charger_manager *cm) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci union power_supply_propval val; 15462306a36Sopenharmony_ci struct power_supply *psy; 15562306a36Sopenharmony_ci bool online = false; 15662306a36Sopenharmony_ci int i, ret; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci /* If at least one of them has one, it's yes. */ 15962306a36Sopenharmony_ci for (i = 0; cm->desc->psy_charger_stat[i]; i++) { 16062306a36Sopenharmony_ci psy = power_supply_get_by_name(cm->desc->psy_charger_stat[i]); 16162306a36Sopenharmony_ci if (!psy) { 16262306a36Sopenharmony_ci dev_err(cm->dev, "Cannot find power supply \"%s\"\n", 16362306a36Sopenharmony_ci cm->desc->psy_charger_stat[i]); 16462306a36Sopenharmony_ci continue; 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_ONLINE, 16862306a36Sopenharmony_ci &val); 16962306a36Sopenharmony_ci power_supply_put(psy); 17062306a36Sopenharmony_ci if (ret == 0 && val.intval) { 17162306a36Sopenharmony_ci online = true; 17262306a36Sopenharmony_ci break; 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci return online; 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci/** 18062306a36Sopenharmony_ci * get_batt_uV - Get the voltage level of the battery 18162306a36Sopenharmony_ci * @cm: the Charger Manager representing the battery. 18262306a36Sopenharmony_ci * @uV: the voltage level returned. 18362306a36Sopenharmony_ci * 18462306a36Sopenharmony_ci * Returns 0 if there is no error. 18562306a36Sopenharmony_ci * Returns a negative value on error. 18662306a36Sopenharmony_ci */ 18762306a36Sopenharmony_cistatic int get_batt_uV(struct charger_manager *cm, int *uV) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci union power_supply_propval val; 19062306a36Sopenharmony_ci struct power_supply *fuel_gauge; 19162306a36Sopenharmony_ci int ret; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge); 19462306a36Sopenharmony_ci if (!fuel_gauge) 19562306a36Sopenharmony_ci return -ENODEV; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci ret = power_supply_get_property(fuel_gauge, 19862306a36Sopenharmony_ci POWER_SUPPLY_PROP_VOLTAGE_NOW, &val); 19962306a36Sopenharmony_ci power_supply_put(fuel_gauge); 20062306a36Sopenharmony_ci if (ret) 20162306a36Sopenharmony_ci return ret; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci *uV = val.intval; 20462306a36Sopenharmony_ci return 0; 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci/** 20862306a36Sopenharmony_ci * is_charging - Returns true if the battery is being charged. 20962306a36Sopenharmony_ci * @cm: the Charger Manager representing the battery. 21062306a36Sopenharmony_ci */ 21162306a36Sopenharmony_cistatic bool is_charging(struct charger_manager *cm) 21262306a36Sopenharmony_ci{ 21362306a36Sopenharmony_ci int i, ret; 21462306a36Sopenharmony_ci bool charging = false; 21562306a36Sopenharmony_ci struct power_supply *psy; 21662306a36Sopenharmony_ci union power_supply_propval val; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci /* If there is no battery, it cannot be charged */ 21962306a36Sopenharmony_ci if (!is_batt_present(cm)) 22062306a36Sopenharmony_ci return false; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci /* If at least one of the charger is charging, return yes */ 22362306a36Sopenharmony_ci for (i = 0; cm->desc->psy_charger_stat[i]; i++) { 22462306a36Sopenharmony_ci /* 1. The charger sholuld not be DISABLED */ 22562306a36Sopenharmony_ci if (cm->emergency_stop) 22662306a36Sopenharmony_ci continue; 22762306a36Sopenharmony_ci if (!cm->charger_enabled) 22862306a36Sopenharmony_ci continue; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci psy = power_supply_get_by_name(cm->desc->psy_charger_stat[i]); 23162306a36Sopenharmony_ci if (!psy) { 23262306a36Sopenharmony_ci dev_err(cm->dev, "Cannot find power supply \"%s\"\n", 23362306a36Sopenharmony_ci cm->desc->psy_charger_stat[i]); 23462306a36Sopenharmony_ci continue; 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci /* 2. The charger should be online (ext-power) */ 23862306a36Sopenharmony_ci ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_ONLINE, 23962306a36Sopenharmony_ci &val); 24062306a36Sopenharmony_ci if (ret) { 24162306a36Sopenharmony_ci dev_warn(cm->dev, "Cannot read ONLINE value from %s\n", 24262306a36Sopenharmony_ci cm->desc->psy_charger_stat[i]); 24362306a36Sopenharmony_ci power_supply_put(psy); 24462306a36Sopenharmony_ci continue; 24562306a36Sopenharmony_ci } 24662306a36Sopenharmony_ci if (val.intval == 0) { 24762306a36Sopenharmony_ci power_supply_put(psy); 24862306a36Sopenharmony_ci continue; 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci /* 25262306a36Sopenharmony_ci * 3. The charger should not be FULL, DISCHARGING, 25362306a36Sopenharmony_ci * or NOT_CHARGING. 25462306a36Sopenharmony_ci */ 25562306a36Sopenharmony_ci ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_STATUS, 25662306a36Sopenharmony_ci &val); 25762306a36Sopenharmony_ci power_supply_put(psy); 25862306a36Sopenharmony_ci if (ret) { 25962306a36Sopenharmony_ci dev_warn(cm->dev, "Cannot read STATUS value from %s\n", 26062306a36Sopenharmony_ci cm->desc->psy_charger_stat[i]); 26162306a36Sopenharmony_ci continue; 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci if (val.intval == POWER_SUPPLY_STATUS_FULL || 26462306a36Sopenharmony_ci val.intval == POWER_SUPPLY_STATUS_DISCHARGING || 26562306a36Sopenharmony_ci val.intval == POWER_SUPPLY_STATUS_NOT_CHARGING) 26662306a36Sopenharmony_ci continue; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci /* Then, this is charging. */ 26962306a36Sopenharmony_ci charging = true; 27062306a36Sopenharmony_ci break; 27162306a36Sopenharmony_ci } 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci return charging; 27462306a36Sopenharmony_ci} 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci/** 27762306a36Sopenharmony_ci * is_full_charged - Returns true if the battery is fully charged. 27862306a36Sopenharmony_ci * @cm: the Charger Manager representing the battery. 27962306a36Sopenharmony_ci */ 28062306a36Sopenharmony_cistatic bool is_full_charged(struct charger_manager *cm) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci struct charger_desc *desc = cm->desc; 28362306a36Sopenharmony_ci union power_supply_propval val; 28462306a36Sopenharmony_ci struct power_supply *fuel_gauge; 28562306a36Sopenharmony_ci bool is_full = false; 28662306a36Sopenharmony_ci int ret = 0; 28762306a36Sopenharmony_ci int uV; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci /* If there is no battery, it cannot be charged */ 29062306a36Sopenharmony_ci if (!is_batt_present(cm)) 29162306a36Sopenharmony_ci return false; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge); 29462306a36Sopenharmony_ci if (!fuel_gauge) 29562306a36Sopenharmony_ci return false; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci /* Full, if it's over the fullbatt voltage */ 29862306a36Sopenharmony_ci if (desc->fullbatt_uV > 0) { 29962306a36Sopenharmony_ci ret = get_batt_uV(cm, &uV); 30062306a36Sopenharmony_ci if (!ret) { 30162306a36Sopenharmony_ci /* Battery is already full, checks voltage drop. */ 30262306a36Sopenharmony_ci if (cm->battery_status == POWER_SUPPLY_STATUS_FULL 30362306a36Sopenharmony_ci && desc->fullbatt_vchkdrop_uV) 30462306a36Sopenharmony_ci uV += desc->fullbatt_vchkdrop_uV; 30562306a36Sopenharmony_ci if (uV >= desc->fullbatt_uV) 30662306a36Sopenharmony_ci return true; 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci } 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci if (desc->fullbatt_full_capacity > 0) { 31162306a36Sopenharmony_ci val.intval = 0; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci /* Not full if capacity of fuel gauge isn't full */ 31462306a36Sopenharmony_ci ret = power_supply_get_property(fuel_gauge, 31562306a36Sopenharmony_ci POWER_SUPPLY_PROP_CHARGE_FULL, &val); 31662306a36Sopenharmony_ci if (!ret && val.intval > desc->fullbatt_full_capacity) { 31762306a36Sopenharmony_ci is_full = true; 31862306a36Sopenharmony_ci goto out; 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci /* Full, if the capacity is more than fullbatt_soc */ 32362306a36Sopenharmony_ci if (desc->fullbatt_soc > 0) { 32462306a36Sopenharmony_ci val.intval = 0; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci ret = power_supply_get_property(fuel_gauge, 32762306a36Sopenharmony_ci POWER_SUPPLY_PROP_CAPACITY, &val); 32862306a36Sopenharmony_ci if (!ret && val.intval >= desc->fullbatt_soc) { 32962306a36Sopenharmony_ci is_full = true; 33062306a36Sopenharmony_ci goto out; 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ciout: 33562306a36Sopenharmony_ci power_supply_put(fuel_gauge); 33662306a36Sopenharmony_ci return is_full; 33762306a36Sopenharmony_ci} 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci/** 34062306a36Sopenharmony_ci * is_polling_required - Return true if need to continue polling for this CM. 34162306a36Sopenharmony_ci * @cm: the Charger Manager representing the battery. 34262306a36Sopenharmony_ci */ 34362306a36Sopenharmony_cistatic bool is_polling_required(struct charger_manager *cm) 34462306a36Sopenharmony_ci{ 34562306a36Sopenharmony_ci switch (cm->desc->polling_mode) { 34662306a36Sopenharmony_ci case CM_POLL_DISABLE: 34762306a36Sopenharmony_ci return false; 34862306a36Sopenharmony_ci case CM_POLL_ALWAYS: 34962306a36Sopenharmony_ci return true; 35062306a36Sopenharmony_ci case CM_POLL_EXTERNAL_POWER_ONLY: 35162306a36Sopenharmony_ci return is_ext_pwr_online(cm); 35262306a36Sopenharmony_ci case CM_POLL_CHARGING_ONLY: 35362306a36Sopenharmony_ci return is_charging(cm); 35462306a36Sopenharmony_ci default: 35562306a36Sopenharmony_ci dev_warn(cm->dev, "Incorrect polling_mode (%d)\n", 35662306a36Sopenharmony_ci cm->desc->polling_mode); 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci return false; 36062306a36Sopenharmony_ci} 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci/** 36362306a36Sopenharmony_ci * try_charger_enable - Enable/Disable chargers altogether 36462306a36Sopenharmony_ci * @cm: the Charger Manager representing the battery. 36562306a36Sopenharmony_ci * @enable: true: enable / false: disable 36662306a36Sopenharmony_ci * 36762306a36Sopenharmony_ci * Note that Charger Manager keeps the charger enabled regardless whether 36862306a36Sopenharmony_ci * the charger is charging or not (because battery is full or no external 36962306a36Sopenharmony_ci * power source exists) except when CM needs to disable chargers forcibly 37062306a36Sopenharmony_ci * because of emergency causes; when the battery is overheated or too cold. 37162306a36Sopenharmony_ci */ 37262306a36Sopenharmony_cistatic int try_charger_enable(struct charger_manager *cm, bool enable) 37362306a36Sopenharmony_ci{ 37462306a36Sopenharmony_ci int err = 0, i; 37562306a36Sopenharmony_ci struct charger_desc *desc = cm->desc; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci /* Ignore if it's redundant command */ 37862306a36Sopenharmony_ci if (enable == cm->charger_enabled) 37962306a36Sopenharmony_ci return 0; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci if (enable) { 38262306a36Sopenharmony_ci if (cm->emergency_stop) 38362306a36Sopenharmony_ci return -EAGAIN; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci /* 38662306a36Sopenharmony_ci * Save start time of charging to limit 38762306a36Sopenharmony_ci * maximum possible charging time. 38862306a36Sopenharmony_ci */ 38962306a36Sopenharmony_ci cm->charging_start_time = ktime_to_ms(ktime_get()); 39062306a36Sopenharmony_ci cm->charging_end_time = 0; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci for (i = 0 ; i < desc->num_charger_regulators ; i++) { 39362306a36Sopenharmony_ci if (desc->charger_regulators[i].externally_control) 39462306a36Sopenharmony_ci continue; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci err = regulator_enable(desc->charger_regulators[i].consumer); 39762306a36Sopenharmony_ci if (err < 0) { 39862306a36Sopenharmony_ci dev_warn(cm->dev, "Cannot enable %s regulator\n", 39962306a36Sopenharmony_ci desc->charger_regulators[i].regulator_name); 40062306a36Sopenharmony_ci } 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci } else { 40362306a36Sopenharmony_ci /* 40462306a36Sopenharmony_ci * Save end time of charging to maintain fully charged state 40562306a36Sopenharmony_ci * of battery after full-batt. 40662306a36Sopenharmony_ci */ 40762306a36Sopenharmony_ci cm->charging_start_time = 0; 40862306a36Sopenharmony_ci cm->charging_end_time = ktime_to_ms(ktime_get()); 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci for (i = 0 ; i < desc->num_charger_regulators ; i++) { 41162306a36Sopenharmony_ci if (desc->charger_regulators[i].externally_control) 41262306a36Sopenharmony_ci continue; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci err = regulator_disable(desc->charger_regulators[i].consumer); 41562306a36Sopenharmony_ci if (err < 0) { 41662306a36Sopenharmony_ci dev_warn(cm->dev, "Cannot disable %s regulator\n", 41762306a36Sopenharmony_ci desc->charger_regulators[i].regulator_name); 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_ci } 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci /* 42262306a36Sopenharmony_ci * Abnormal battery state - Stop charging forcibly, 42362306a36Sopenharmony_ci * even if charger was enabled at the other places 42462306a36Sopenharmony_ci */ 42562306a36Sopenharmony_ci for (i = 0; i < desc->num_charger_regulators; i++) { 42662306a36Sopenharmony_ci if (regulator_is_enabled( 42762306a36Sopenharmony_ci desc->charger_regulators[i].consumer)) { 42862306a36Sopenharmony_ci regulator_force_disable( 42962306a36Sopenharmony_ci desc->charger_regulators[i].consumer); 43062306a36Sopenharmony_ci dev_warn(cm->dev, "Disable regulator(%s) forcibly\n", 43162306a36Sopenharmony_ci desc->charger_regulators[i].regulator_name); 43262306a36Sopenharmony_ci } 43362306a36Sopenharmony_ci } 43462306a36Sopenharmony_ci } 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci if (!err) 43762306a36Sopenharmony_ci cm->charger_enabled = enable; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci return err; 44062306a36Sopenharmony_ci} 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci/** 44362306a36Sopenharmony_ci * check_charging_duration - Monitor charging/discharging duration 44462306a36Sopenharmony_ci * @cm: the Charger Manager representing the battery. 44562306a36Sopenharmony_ci * 44662306a36Sopenharmony_ci * If whole charging duration exceed 'charging_max_duration_ms', 44762306a36Sopenharmony_ci * cm stop charging to prevent overcharge/overheat. If discharging 44862306a36Sopenharmony_ci * duration exceed 'discharging _max_duration_ms', charger cable is 44962306a36Sopenharmony_ci * attached, after full-batt, cm start charging to maintain fully 45062306a36Sopenharmony_ci * charged state for battery. 45162306a36Sopenharmony_ci */ 45262306a36Sopenharmony_cistatic int check_charging_duration(struct charger_manager *cm) 45362306a36Sopenharmony_ci{ 45462306a36Sopenharmony_ci struct charger_desc *desc = cm->desc; 45562306a36Sopenharmony_ci u64 curr = ktime_to_ms(ktime_get()); 45662306a36Sopenharmony_ci u64 duration; 45762306a36Sopenharmony_ci int ret = false; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci if (!desc->charging_max_duration_ms && 46062306a36Sopenharmony_ci !desc->discharging_max_duration_ms) 46162306a36Sopenharmony_ci return ret; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci if (cm->charger_enabled) { 46462306a36Sopenharmony_ci duration = curr - cm->charging_start_time; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci if (duration > desc->charging_max_duration_ms) { 46762306a36Sopenharmony_ci dev_info(cm->dev, "Charging duration exceed %ums\n", 46862306a36Sopenharmony_ci desc->charging_max_duration_ms); 46962306a36Sopenharmony_ci ret = true; 47062306a36Sopenharmony_ci } 47162306a36Sopenharmony_ci } else if (cm->battery_status == POWER_SUPPLY_STATUS_NOT_CHARGING) { 47262306a36Sopenharmony_ci duration = curr - cm->charging_end_time; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci if (duration > desc->discharging_max_duration_ms) { 47562306a36Sopenharmony_ci dev_info(cm->dev, "Discharging duration exceed %ums\n", 47662306a36Sopenharmony_ci desc->discharging_max_duration_ms); 47762306a36Sopenharmony_ci ret = true; 47862306a36Sopenharmony_ci } 47962306a36Sopenharmony_ci } 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci return ret; 48262306a36Sopenharmony_ci} 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_cistatic int cm_get_battery_temperature_by_psy(struct charger_manager *cm, 48562306a36Sopenharmony_ci int *temp) 48662306a36Sopenharmony_ci{ 48762306a36Sopenharmony_ci struct power_supply *fuel_gauge; 48862306a36Sopenharmony_ci int ret; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge); 49162306a36Sopenharmony_ci if (!fuel_gauge) 49262306a36Sopenharmony_ci return -ENODEV; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci ret = power_supply_get_property(fuel_gauge, 49562306a36Sopenharmony_ci POWER_SUPPLY_PROP_TEMP, 49662306a36Sopenharmony_ci (union power_supply_propval *)temp); 49762306a36Sopenharmony_ci power_supply_put(fuel_gauge); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci return ret; 50062306a36Sopenharmony_ci} 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_cistatic int cm_get_battery_temperature(struct charger_manager *cm, 50362306a36Sopenharmony_ci int *temp) 50462306a36Sopenharmony_ci{ 50562306a36Sopenharmony_ci int ret; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci if (!cm->desc->measure_battery_temp) 50862306a36Sopenharmony_ci return -ENODEV; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci#ifdef CONFIG_THERMAL 51162306a36Sopenharmony_ci if (cm->tzd_batt) { 51262306a36Sopenharmony_ci ret = thermal_zone_get_temp(cm->tzd_batt, temp); 51362306a36Sopenharmony_ci if (!ret) 51462306a36Sopenharmony_ci /* Calibrate temperature unit */ 51562306a36Sopenharmony_ci *temp /= 100; 51662306a36Sopenharmony_ci } else 51762306a36Sopenharmony_ci#endif 51862306a36Sopenharmony_ci { 51962306a36Sopenharmony_ci /* if-else continued from CONFIG_THERMAL */ 52062306a36Sopenharmony_ci ret = cm_get_battery_temperature_by_psy(cm, temp); 52162306a36Sopenharmony_ci } 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci return ret; 52462306a36Sopenharmony_ci} 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_cistatic int cm_check_thermal_status(struct charger_manager *cm) 52762306a36Sopenharmony_ci{ 52862306a36Sopenharmony_ci struct charger_desc *desc = cm->desc; 52962306a36Sopenharmony_ci int temp, upper_limit, lower_limit; 53062306a36Sopenharmony_ci int ret = 0; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci ret = cm_get_battery_temperature(cm, &temp); 53362306a36Sopenharmony_ci if (ret) { 53462306a36Sopenharmony_ci /* FIXME: 53562306a36Sopenharmony_ci * No information of battery temperature might 53662306a36Sopenharmony_ci * occur hazardous result. We have to handle it 53762306a36Sopenharmony_ci * depending on battery type. 53862306a36Sopenharmony_ci */ 53962306a36Sopenharmony_ci dev_err(cm->dev, "Failed to get battery temperature\n"); 54062306a36Sopenharmony_ci return 0; 54162306a36Sopenharmony_ci } 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci upper_limit = desc->temp_max; 54462306a36Sopenharmony_ci lower_limit = desc->temp_min; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci if (cm->emergency_stop) { 54762306a36Sopenharmony_ci upper_limit -= desc->temp_diff; 54862306a36Sopenharmony_ci lower_limit += desc->temp_diff; 54962306a36Sopenharmony_ci } 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci if (temp > upper_limit) 55262306a36Sopenharmony_ci ret = CM_BATT_OVERHEAT; 55362306a36Sopenharmony_ci else if (temp < lower_limit) 55462306a36Sopenharmony_ci ret = CM_BATT_COLD; 55562306a36Sopenharmony_ci else 55662306a36Sopenharmony_ci ret = CM_BATT_OK; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci cm->emergency_stop = ret; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci return ret; 56162306a36Sopenharmony_ci} 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci/** 56462306a36Sopenharmony_ci * cm_get_target_status - Check current status and get next target status. 56562306a36Sopenharmony_ci * @cm: the Charger Manager representing the battery. 56662306a36Sopenharmony_ci */ 56762306a36Sopenharmony_cistatic int cm_get_target_status(struct charger_manager *cm) 56862306a36Sopenharmony_ci{ 56962306a36Sopenharmony_ci if (!is_ext_pwr_online(cm)) 57062306a36Sopenharmony_ci return POWER_SUPPLY_STATUS_DISCHARGING; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci if (cm_check_thermal_status(cm)) { 57362306a36Sopenharmony_ci /* Check if discharging duration exceeds limit. */ 57462306a36Sopenharmony_ci if (check_charging_duration(cm)) 57562306a36Sopenharmony_ci goto charging_ok; 57662306a36Sopenharmony_ci return POWER_SUPPLY_STATUS_NOT_CHARGING; 57762306a36Sopenharmony_ci } 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci switch (cm->battery_status) { 58062306a36Sopenharmony_ci case POWER_SUPPLY_STATUS_CHARGING: 58162306a36Sopenharmony_ci /* Check if charging duration exceeds limit. */ 58262306a36Sopenharmony_ci if (check_charging_duration(cm)) 58362306a36Sopenharmony_ci return POWER_SUPPLY_STATUS_FULL; 58462306a36Sopenharmony_ci fallthrough; 58562306a36Sopenharmony_ci case POWER_SUPPLY_STATUS_FULL: 58662306a36Sopenharmony_ci if (is_full_charged(cm)) 58762306a36Sopenharmony_ci return POWER_SUPPLY_STATUS_FULL; 58862306a36Sopenharmony_ci fallthrough; 58962306a36Sopenharmony_ci default: 59062306a36Sopenharmony_ci break; 59162306a36Sopenharmony_ci } 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_cicharging_ok: 59462306a36Sopenharmony_ci /* Charging is allowed. */ 59562306a36Sopenharmony_ci return POWER_SUPPLY_STATUS_CHARGING; 59662306a36Sopenharmony_ci} 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci/** 59962306a36Sopenharmony_ci * _cm_monitor - Monitor the temperature and return true for exceptions. 60062306a36Sopenharmony_ci * @cm: the Charger Manager representing the battery. 60162306a36Sopenharmony_ci * 60262306a36Sopenharmony_ci * Returns true if there is an event to notify for the battery. 60362306a36Sopenharmony_ci * (True if the status of "emergency_stop" changes) 60462306a36Sopenharmony_ci */ 60562306a36Sopenharmony_cistatic bool _cm_monitor(struct charger_manager *cm) 60662306a36Sopenharmony_ci{ 60762306a36Sopenharmony_ci int target; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci target = cm_get_target_status(cm); 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci try_charger_enable(cm, (target == POWER_SUPPLY_STATUS_CHARGING)); 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci if (cm->battery_status != target) { 61462306a36Sopenharmony_ci cm->battery_status = target; 61562306a36Sopenharmony_ci power_supply_changed(cm->charger_psy); 61662306a36Sopenharmony_ci } 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci return (cm->battery_status == POWER_SUPPLY_STATUS_NOT_CHARGING); 61962306a36Sopenharmony_ci} 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci/** 62262306a36Sopenharmony_ci * cm_monitor - Monitor every battery. 62362306a36Sopenharmony_ci * 62462306a36Sopenharmony_ci * Returns true if there is an event to notify from any of the batteries. 62562306a36Sopenharmony_ci * (True if the status of "emergency_stop" changes) 62662306a36Sopenharmony_ci */ 62762306a36Sopenharmony_cistatic bool cm_monitor(void) 62862306a36Sopenharmony_ci{ 62962306a36Sopenharmony_ci bool stop = false; 63062306a36Sopenharmony_ci struct charger_manager *cm; 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci mutex_lock(&cm_list_mtx); 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci list_for_each_entry(cm, &cm_list, entry) { 63562306a36Sopenharmony_ci if (_cm_monitor(cm)) 63662306a36Sopenharmony_ci stop = true; 63762306a36Sopenharmony_ci } 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci mutex_unlock(&cm_list_mtx); 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci return stop; 64262306a36Sopenharmony_ci} 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci/** 64562306a36Sopenharmony_ci * _setup_polling - Setup the next instance of polling. 64662306a36Sopenharmony_ci * @work: work_struct of the function _setup_polling. 64762306a36Sopenharmony_ci */ 64862306a36Sopenharmony_cistatic void _setup_polling(struct work_struct *work) 64962306a36Sopenharmony_ci{ 65062306a36Sopenharmony_ci unsigned long min = ULONG_MAX; 65162306a36Sopenharmony_ci struct charger_manager *cm; 65262306a36Sopenharmony_ci bool keep_polling = false; 65362306a36Sopenharmony_ci unsigned long _next_polling; 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci mutex_lock(&cm_list_mtx); 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci list_for_each_entry(cm, &cm_list, entry) { 65862306a36Sopenharmony_ci if (is_polling_required(cm) && cm->desc->polling_interval_ms) { 65962306a36Sopenharmony_ci keep_polling = true; 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci if (min > cm->desc->polling_interval_ms) 66262306a36Sopenharmony_ci min = cm->desc->polling_interval_ms; 66362306a36Sopenharmony_ci } 66462306a36Sopenharmony_ci } 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci polling_jiffy = msecs_to_jiffies(min); 66762306a36Sopenharmony_ci if (polling_jiffy <= CM_JIFFIES_SMALL) 66862306a36Sopenharmony_ci polling_jiffy = CM_JIFFIES_SMALL + 1; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci if (!keep_polling) 67162306a36Sopenharmony_ci polling_jiffy = ULONG_MAX; 67262306a36Sopenharmony_ci if (polling_jiffy == ULONG_MAX) 67362306a36Sopenharmony_ci goto out; 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci WARN(cm_wq == NULL, "charger-manager: workqueue not initialized" 67662306a36Sopenharmony_ci ". try it later. %s\n", __func__); 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci /* 67962306a36Sopenharmony_ci * Use mod_delayed_work() iff the next polling interval should 68062306a36Sopenharmony_ci * occur before the currently scheduled one. If @cm_monitor_work 68162306a36Sopenharmony_ci * isn't active, the end result is the same, so no need to worry 68262306a36Sopenharmony_ci * about stale @next_polling. 68362306a36Sopenharmony_ci */ 68462306a36Sopenharmony_ci _next_polling = jiffies + polling_jiffy; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci if (time_before(_next_polling, next_polling)) { 68762306a36Sopenharmony_ci mod_delayed_work(cm_wq, &cm_monitor_work, polling_jiffy); 68862306a36Sopenharmony_ci next_polling = _next_polling; 68962306a36Sopenharmony_ci } else { 69062306a36Sopenharmony_ci if (queue_delayed_work(cm_wq, &cm_monitor_work, polling_jiffy)) 69162306a36Sopenharmony_ci next_polling = _next_polling; 69262306a36Sopenharmony_ci } 69362306a36Sopenharmony_ciout: 69462306a36Sopenharmony_ci mutex_unlock(&cm_list_mtx); 69562306a36Sopenharmony_ci} 69662306a36Sopenharmony_cistatic DECLARE_WORK(setup_polling, _setup_polling); 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci/** 69962306a36Sopenharmony_ci * cm_monitor_poller - The Monitor / Poller. 70062306a36Sopenharmony_ci * @work: work_struct of the function cm_monitor_poller 70162306a36Sopenharmony_ci * 70262306a36Sopenharmony_ci * During non-suspended state, cm_monitor_poller is used to poll and monitor 70362306a36Sopenharmony_ci * the batteries. 70462306a36Sopenharmony_ci */ 70562306a36Sopenharmony_cistatic void cm_monitor_poller(struct work_struct *work) 70662306a36Sopenharmony_ci{ 70762306a36Sopenharmony_ci cm_monitor(); 70862306a36Sopenharmony_ci schedule_work(&setup_polling); 70962306a36Sopenharmony_ci} 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_cistatic int charger_get_property(struct power_supply *psy, 71262306a36Sopenharmony_ci enum power_supply_property psp, 71362306a36Sopenharmony_ci union power_supply_propval *val) 71462306a36Sopenharmony_ci{ 71562306a36Sopenharmony_ci struct charger_manager *cm = power_supply_get_drvdata(psy); 71662306a36Sopenharmony_ci struct charger_desc *desc = cm->desc; 71762306a36Sopenharmony_ci struct power_supply *fuel_gauge = NULL; 71862306a36Sopenharmony_ci int ret = 0; 71962306a36Sopenharmony_ci int uV; 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci switch (psp) { 72262306a36Sopenharmony_ci case POWER_SUPPLY_PROP_STATUS: 72362306a36Sopenharmony_ci val->intval = cm->battery_status; 72462306a36Sopenharmony_ci break; 72562306a36Sopenharmony_ci case POWER_SUPPLY_PROP_HEALTH: 72662306a36Sopenharmony_ci if (cm->emergency_stop == CM_BATT_OVERHEAT) 72762306a36Sopenharmony_ci val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; 72862306a36Sopenharmony_ci else if (cm->emergency_stop == CM_BATT_COLD) 72962306a36Sopenharmony_ci val->intval = POWER_SUPPLY_HEALTH_COLD; 73062306a36Sopenharmony_ci else 73162306a36Sopenharmony_ci val->intval = POWER_SUPPLY_HEALTH_GOOD; 73262306a36Sopenharmony_ci break; 73362306a36Sopenharmony_ci case POWER_SUPPLY_PROP_PRESENT: 73462306a36Sopenharmony_ci if (is_batt_present(cm)) 73562306a36Sopenharmony_ci val->intval = 1; 73662306a36Sopenharmony_ci else 73762306a36Sopenharmony_ci val->intval = 0; 73862306a36Sopenharmony_ci break; 73962306a36Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_NOW: 74062306a36Sopenharmony_ci ret = get_batt_uV(cm, &val->intval); 74162306a36Sopenharmony_ci break; 74262306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CURRENT_NOW: 74362306a36Sopenharmony_ci fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge); 74462306a36Sopenharmony_ci if (!fuel_gauge) { 74562306a36Sopenharmony_ci ret = -ENODEV; 74662306a36Sopenharmony_ci break; 74762306a36Sopenharmony_ci } 74862306a36Sopenharmony_ci ret = power_supply_get_property(fuel_gauge, 74962306a36Sopenharmony_ci POWER_SUPPLY_PROP_CURRENT_NOW, val); 75062306a36Sopenharmony_ci break; 75162306a36Sopenharmony_ci case POWER_SUPPLY_PROP_TEMP: 75262306a36Sopenharmony_ci return cm_get_battery_temperature(cm, &val->intval); 75362306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CAPACITY: 75462306a36Sopenharmony_ci if (!is_batt_present(cm)) { 75562306a36Sopenharmony_ci /* There is no battery. Assume 100% */ 75662306a36Sopenharmony_ci val->intval = 100; 75762306a36Sopenharmony_ci break; 75862306a36Sopenharmony_ci } 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge); 76162306a36Sopenharmony_ci if (!fuel_gauge) { 76262306a36Sopenharmony_ci ret = -ENODEV; 76362306a36Sopenharmony_ci break; 76462306a36Sopenharmony_ci } 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci ret = power_supply_get_property(fuel_gauge, 76762306a36Sopenharmony_ci POWER_SUPPLY_PROP_CAPACITY, val); 76862306a36Sopenharmony_ci if (ret) 76962306a36Sopenharmony_ci break; 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci if (val->intval > 100) { 77262306a36Sopenharmony_ci val->intval = 100; 77362306a36Sopenharmony_ci break; 77462306a36Sopenharmony_ci } 77562306a36Sopenharmony_ci if (val->intval < 0) 77662306a36Sopenharmony_ci val->intval = 0; 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci /* Do not adjust SOC when charging: voltage is overrated */ 77962306a36Sopenharmony_ci if (is_charging(cm)) 78062306a36Sopenharmony_ci break; 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci /* 78362306a36Sopenharmony_ci * If the capacity value is inconsistent, calibrate it base on 78462306a36Sopenharmony_ci * the battery voltage values and the thresholds given as desc 78562306a36Sopenharmony_ci */ 78662306a36Sopenharmony_ci ret = get_batt_uV(cm, &uV); 78762306a36Sopenharmony_ci if (ret) { 78862306a36Sopenharmony_ci /* Voltage information not available. No calibration */ 78962306a36Sopenharmony_ci ret = 0; 79062306a36Sopenharmony_ci break; 79162306a36Sopenharmony_ci } 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci if (desc->fullbatt_uV > 0 && uV >= desc->fullbatt_uV && 79462306a36Sopenharmony_ci !is_charging(cm)) { 79562306a36Sopenharmony_ci val->intval = 100; 79662306a36Sopenharmony_ci break; 79762306a36Sopenharmony_ci } 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci break; 80062306a36Sopenharmony_ci case POWER_SUPPLY_PROP_ONLINE: 80162306a36Sopenharmony_ci if (is_ext_pwr_online(cm)) 80262306a36Sopenharmony_ci val->intval = 1; 80362306a36Sopenharmony_ci else 80462306a36Sopenharmony_ci val->intval = 0; 80562306a36Sopenharmony_ci break; 80662306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CHARGE_FULL: 80762306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CHARGE_NOW: 80862306a36Sopenharmony_ci fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge); 80962306a36Sopenharmony_ci if (!fuel_gauge) { 81062306a36Sopenharmony_ci ret = -ENODEV; 81162306a36Sopenharmony_ci break; 81262306a36Sopenharmony_ci } 81362306a36Sopenharmony_ci ret = power_supply_get_property(fuel_gauge, psp, val); 81462306a36Sopenharmony_ci break; 81562306a36Sopenharmony_ci default: 81662306a36Sopenharmony_ci return -EINVAL; 81762306a36Sopenharmony_ci } 81862306a36Sopenharmony_ci if (fuel_gauge) 81962306a36Sopenharmony_ci power_supply_put(fuel_gauge); 82062306a36Sopenharmony_ci return ret; 82162306a36Sopenharmony_ci} 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci#define NUM_CHARGER_PSY_OPTIONAL (4) 82462306a36Sopenharmony_cistatic enum power_supply_property default_charger_props[] = { 82562306a36Sopenharmony_ci /* Guaranteed to provide */ 82662306a36Sopenharmony_ci POWER_SUPPLY_PROP_STATUS, 82762306a36Sopenharmony_ci POWER_SUPPLY_PROP_HEALTH, 82862306a36Sopenharmony_ci POWER_SUPPLY_PROP_PRESENT, 82962306a36Sopenharmony_ci POWER_SUPPLY_PROP_VOLTAGE_NOW, 83062306a36Sopenharmony_ci POWER_SUPPLY_PROP_CAPACITY, 83162306a36Sopenharmony_ci POWER_SUPPLY_PROP_ONLINE, 83262306a36Sopenharmony_ci /* 83362306a36Sopenharmony_ci * Optional properties are: 83462306a36Sopenharmony_ci * POWER_SUPPLY_PROP_CHARGE_FULL, 83562306a36Sopenharmony_ci * POWER_SUPPLY_PROP_CHARGE_NOW, 83662306a36Sopenharmony_ci * POWER_SUPPLY_PROP_CURRENT_NOW, 83762306a36Sopenharmony_ci * POWER_SUPPLY_PROP_TEMP, 83862306a36Sopenharmony_ci */ 83962306a36Sopenharmony_ci}; 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_cistatic const struct power_supply_desc psy_default = { 84262306a36Sopenharmony_ci .name = "battery", 84362306a36Sopenharmony_ci .type = POWER_SUPPLY_TYPE_BATTERY, 84462306a36Sopenharmony_ci .properties = default_charger_props, 84562306a36Sopenharmony_ci .num_properties = ARRAY_SIZE(default_charger_props), 84662306a36Sopenharmony_ci .get_property = charger_get_property, 84762306a36Sopenharmony_ci .no_thermal = true, 84862306a36Sopenharmony_ci}; 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci/** 85162306a36Sopenharmony_ci * cm_setup_timer - For in-suspend monitoring setup wakeup alarm 85262306a36Sopenharmony_ci * for suspend_again. 85362306a36Sopenharmony_ci * 85462306a36Sopenharmony_ci * Returns true if the alarm is set for Charger Manager to use. 85562306a36Sopenharmony_ci * Returns false if 85662306a36Sopenharmony_ci * cm_setup_timer fails to set an alarm, 85762306a36Sopenharmony_ci * cm_setup_timer does not need to set an alarm for Charger Manager, 85862306a36Sopenharmony_ci * or an alarm previously configured is to be used. 85962306a36Sopenharmony_ci */ 86062306a36Sopenharmony_cistatic bool cm_setup_timer(void) 86162306a36Sopenharmony_ci{ 86262306a36Sopenharmony_ci struct charger_manager *cm; 86362306a36Sopenharmony_ci unsigned int wakeup_ms = UINT_MAX; 86462306a36Sopenharmony_ci int timer_req = 0; 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci if (time_after(next_polling, jiffies)) 86762306a36Sopenharmony_ci CM_MIN_VALID(wakeup_ms, 86862306a36Sopenharmony_ci jiffies_to_msecs(next_polling - jiffies)); 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci mutex_lock(&cm_list_mtx); 87162306a36Sopenharmony_ci list_for_each_entry(cm, &cm_list, entry) { 87262306a36Sopenharmony_ci /* Skip if polling is not required for this CM */ 87362306a36Sopenharmony_ci if (!is_polling_required(cm) && !cm->emergency_stop) 87462306a36Sopenharmony_ci continue; 87562306a36Sopenharmony_ci timer_req++; 87662306a36Sopenharmony_ci if (cm->desc->polling_interval_ms == 0) 87762306a36Sopenharmony_ci continue; 87862306a36Sopenharmony_ci CM_MIN_VALID(wakeup_ms, cm->desc->polling_interval_ms); 87962306a36Sopenharmony_ci } 88062306a36Sopenharmony_ci mutex_unlock(&cm_list_mtx); 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci if (timer_req && cm_timer) { 88362306a36Sopenharmony_ci ktime_t now, add; 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci /* 88662306a36Sopenharmony_ci * Set alarm with the polling interval (wakeup_ms) 88762306a36Sopenharmony_ci * The alarm time should be NOW + CM_RTC_SMALL or later. 88862306a36Sopenharmony_ci */ 88962306a36Sopenharmony_ci if (wakeup_ms == UINT_MAX || 89062306a36Sopenharmony_ci wakeup_ms < CM_RTC_SMALL * MSEC_PER_SEC) 89162306a36Sopenharmony_ci wakeup_ms = 2 * CM_RTC_SMALL * MSEC_PER_SEC; 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci pr_info("Charger Manager wakeup timer: %u ms\n", wakeup_ms); 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci now = ktime_get_boottime(); 89662306a36Sopenharmony_ci add = ktime_set(wakeup_ms / MSEC_PER_SEC, 89762306a36Sopenharmony_ci (wakeup_ms % MSEC_PER_SEC) * NSEC_PER_MSEC); 89862306a36Sopenharmony_ci alarm_start(cm_timer, ktime_add(now, add)); 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci cm_suspend_duration_ms = wakeup_ms; 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci return true; 90362306a36Sopenharmony_ci } 90462306a36Sopenharmony_ci return false; 90562306a36Sopenharmony_ci} 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci/** 90862306a36Sopenharmony_ci * charger_extcon_work - enable/diable charger according to the state 90962306a36Sopenharmony_ci * of charger cable 91062306a36Sopenharmony_ci * 91162306a36Sopenharmony_ci * @work: work_struct of the function charger_extcon_work. 91262306a36Sopenharmony_ci */ 91362306a36Sopenharmony_cistatic void charger_extcon_work(struct work_struct *work) 91462306a36Sopenharmony_ci{ 91562306a36Sopenharmony_ci struct charger_cable *cable = 91662306a36Sopenharmony_ci container_of(work, struct charger_cable, wq); 91762306a36Sopenharmony_ci int ret; 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci if (cable->attached && cable->min_uA != 0 && cable->max_uA != 0) { 92062306a36Sopenharmony_ci ret = regulator_set_current_limit(cable->charger->consumer, 92162306a36Sopenharmony_ci cable->min_uA, cable->max_uA); 92262306a36Sopenharmony_ci if (ret < 0) { 92362306a36Sopenharmony_ci pr_err("Cannot set current limit of %s (%s)\n", 92462306a36Sopenharmony_ci cable->charger->regulator_name, cable->name); 92562306a36Sopenharmony_ci return; 92662306a36Sopenharmony_ci } 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci pr_info("Set current limit of %s : %duA ~ %duA\n", 92962306a36Sopenharmony_ci cable->charger->regulator_name, 93062306a36Sopenharmony_ci cable->min_uA, cable->max_uA); 93162306a36Sopenharmony_ci } 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci cancel_delayed_work(&cm_monitor_work); 93462306a36Sopenharmony_ci queue_delayed_work(cm_wq, &cm_monitor_work, 0); 93562306a36Sopenharmony_ci} 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci/** 93862306a36Sopenharmony_ci * charger_extcon_notifier - receive the state of charger cable 93962306a36Sopenharmony_ci * when registered cable is attached or detached. 94062306a36Sopenharmony_ci * 94162306a36Sopenharmony_ci * @self: the notifier block of the charger_extcon_notifier. 94262306a36Sopenharmony_ci * @event: the cable state. 94362306a36Sopenharmony_ci * @ptr: the data pointer of notifier block. 94462306a36Sopenharmony_ci */ 94562306a36Sopenharmony_cistatic int charger_extcon_notifier(struct notifier_block *self, 94662306a36Sopenharmony_ci unsigned long event, void *ptr) 94762306a36Sopenharmony_ci{ 94862306a36Sopenharmony_ci struct charger_cable *cable = 94962306a36Sopenharmony_ci container_of(self, struct charger_cable, nb); 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci /* 95262306a36Sopenharmony_ci * The newly state of charger cable. 95362306a36Sopenharmony_ci * If cable is attached, cable->attached is true. 95462306a36Sopenharmony_ci */ 95562306a36Sopenharmony_ci cable->attached = event; 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci /* 95862306a36Sopenharmony_ci * Setup work for controlling charger(regulator) 95962306a36Sopenharmony_ci * according to charger cable. 96062306a36Sopenharmony_ci */ 96162306a36Sopenharmony_ci schedule_work(&cable->wq); 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci return NOTIFY_DONE; 96462306a36Sopenharmony_ci} 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci/** 96762306a36Sopenharmony_ci * charger_extcon_init - register external connector to use it 96862306a36Sopenharmony_ci * as the charger cable 96962306a36Sopenharmony_ci * 97062306a36Sopenharmony_ci * @cm: the Charger Manager representing the battery. 97162306a36Sopenharmony_ci * @cable: the Charger cable representing the external connector. 97262306a36Sopenharmony_ci */ 97362306a36Sopenharmony_cistatic int charger_extcon_init(struct charger_manager *cm, 97462306a36Sopenharmony_ci struct charger_cable *cable) 97562306a36Sopenharmony_ci{ 97662306a36Sopenharmony_ci int ret, i; 97762306a36Sopenharmony_ci u64 extcon_type = EXTCON_NONE; 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci /* 98062306a36Sopenharmony_ci * Charger manager use Extcon framework to identify 98162306a36Sopenharmony_ci * the charger cable among various external connector 98262306a36Sopenharmony_ci * cable (e.g., TA, USB, MHL, Dock). 98362306a36Sopenharmony_ci */ 98462306a36Sopenharmony_ci INIT_WORK(&cable->wq, charger_extcon_work); 98562306a36Sopenharmony_ci cable->nb.notifier_call = charger_extcon_notifier; 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci cable->extcon_dev = extcon_get_extcon_dev(cable->extcon_name); 98862306a36Sopenharmony_ci if (IS_ERR(cable->extcon_dev)) { 98962306a36Sopenharmony_ci pr_err("Cannot find extcon_dev for %s (cable: %s)\n", 99062306a36Sopenharmony_ci cable->extcon_name, cable->name); 99162306a36Sopenharmony_ci return PTR_ERR(cable->extcon_dev); 99262306a36Sopenharmony_ci } 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(extcon_mapping); i++) { 99562306a36Sopenharmony_ci if (!strcmp(cable->name, extcon_mapping[i].name)) { 99662306a36Sopenharmony_ci extcon_type = extcon_mapping[i].extcon_type; 99762306a36Sopenharmony_ci break; 99862306a36Sopenharmony_ci } 99962306a36Sopenharmony_ci } 100062306a36Sopenharmony_ci if (extcon_type == EXTCON_NONE) { 100162306a36Sopenharmony_ci pr_err("Cannot find cable for type %s", cable->name); 100262306a36Sopenharmony_ci return -EINVAL; 100362306a36Sopenharmony_ci } 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_ci cable->extcon_type = extcon_type; 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci ret = devm_extcon_register_notifier(cm->dev, cable->extcon_dev, 100862306a36Sopenharmony_ci cable->extcon_type, &cable->nb); 100962306a36Sopenharmony_ci if (ret < 0) { 101062306a36Sopenharmony_ci pr_err("Cannot register extcon_dev for %s (cable: %s)\n", 101162306a36Sopenharmony_ci cable->extcon_name, cable->name); 101262306a36Sopenharmony_ci return ret; 101362306a36Sopenharmony_ci } 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci return 0; 101662306a36Sopenharmony_ci} 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci/** 101962306a36Sopenharmony_ci * charger_manager_register_extcon - Register extcon device to receive state 102062306a36Sopenharmony_ci * of charger cable. 102162306a36Sopenharmony_ci * @cm: the Charger Manager representing the battery. 102262306a36Sopenharmony_ci * 102362306a36Sopenharmony_ci * This function support EXTCON(External Connector) subsystem to detect the 102462306a36Sopenharmony_ci * state of charger cables for enabling or disabling charger(regulator) and 102562306a36Sopenharmony_ci * select the charger cable for charging among a number of external cable 102662306a36Sopenharmony_ci * according to policy of H/W board. 102762306a36Sopenharmony_ci */ 102862306a36Sopenharmony_cistatic int charger_manager_register_extcon(struct charger_manager *cm) 102962306a36Sopenharmony_ci{ 103062306a36Sopenharmony_ci struct charger_desc *desc = cm->desc; 103162306a36Sopenharmony_ci struct charger_regulator *charger; 103262306a36Sopenharmony_ci unsigned long event; 103362306a36Sopenharmony_ci int ret; 103462306a36Sopenharmony_ci int i; 103562306a36Sopenharmony_ci int j; 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci for (i = 0; i < desc->num_charger_regulators; i++) { 103862306a36Sopenharmony_ci charger = &desc->charger_regulators[i]; 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci charger->consumer = regulator_get(cm->dev, 104162306a36Sopenharmony_ci charger->regulator_name); 104262306a36Sopenharmony_ci if (IS_ERR(charger->consumer)) { 104362306a36Sopenharmony_ci dev_err(cm->dev, "Cannot find charger(%s)\n", 104462306a36Sopenharmony_ci charger->regulator_name); 104562306a36Sopenharmony_ci return PTR_ERR(charger->consumer); 104662306a36Sopenharmony_ci } 104762306a36Sopenharmony_ci charger->cm = cm; 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci for (j = 0; j < charger->num_cables; j++) { 105062306a36Sopenharmony_ci struct charger_cable *cable = &charger->cables[j]; 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci ret = charger_extcon_init(cm, cable); 105362306a36Sopenharmony_ci if (ret < 0) { 105462306a36Sopenharmony_ci dev_err(cm->dev, "Cannot initialize charger(%s)\n", 105562306a36Sopenharmony_ci charger->regulator_name); 105662306a36Sopenharmony_ci return ret; 105762306a36Sopenharmony_ci } 105862306a36Sopenharmony_ci cable->charger = charger; 105962306a36Sopenharmony_ci cable->cm = cm; 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci event = extcon_get_state(cable->extcon_dev, 106262306a36Sopenharmony_ci cable->extcon_type); 106362306a36Sopenharmony_ci charger_extcon_notifier(&cable->nb, 106462306a36Sopenharmony_ci event, NULL); 106562306a36Sopenharmony_ci } 106662306a36Sopenharmony_ci } 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci return 0; 106962306a36Sopenharmony_ci} 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci/* help function of sysfs node to control charger(regulator) */ 107262306a36Sopenharmony_cistatic ssize_t charger_name_show(struct device *dev, 107362306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 107462306a36Sopenharmony_ci{ 107562306a36Sopenharmony_ci struct charger_regulator *charger 107662306a36Sopenharmony_ci = container_of(attr, struct charger_regulator, attr_name); 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_ci return sysfs_emit(buf, "%s\n", charger->regulator_name); 107962306a36Sopenharmony_ci} 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_cistatic ssize_t charger_state_show(struct device *dev, 108262306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 108362306a36Sopenharmony_ci{ 108462306a36Sopenharmony_ci struct charger_regulator *charger 108562306a36Sopenharmony_ci = container_of(attr, struct charger_regulator, attr_state); 108662306a36Sopenharmony_ci int state = 0; 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_ci if (!charger->externally_control) 108962306a36Sopenharmony_ci state = regulator_is_enabled(charger->consumer); 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci return sysfs_emit(buf, "%s\n", state ? "enabled" : "disabled"); 109262306a36Sopenharmony_ci} 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_cistatic ssize_t charger_externally_control_show(struct device *dev, 109562306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 109662306a36Sopenharmony_ci{ 109762306a36Sopenharmony_ci struct charger_regulator *charger = container_of(attr, 109862306a36Sopenharmony_ci struct charger_regulator, attr_externally_control); 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci return sysfs_emit(buf, "%d\n", charger->externally_control); 110162306a36Sopenharmony_ci} 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_cistatic ssize_t charger_externally_control_store(struct device *dev, 110462306a36Sopenharmony_ci struct device_attribute *attr, const char *buf, 110562306a36Sopenharmony_ci size_t count) 110662306a36Sopenharmony_ci{ 110762306a36Sopenharmony_ci struct charger_regulator *charger 110862306a36Sopenharmony_ci = container_of(attr, struct charger_regulator, 110962306a36Sopenharmony_ci attr_externally_control); 111062306a36Sopenharmony_ci struct charger_manager *cm = charger->cm; 111162306a36Sopenharmony_ci struct charger_desc *desc = cm->desc; 111262306a36Sopenharmony_ci int i; 111362306a36Sopenharmony_ci int ret; 111462306a36Sopenharmony_ci int externally_control; 111562306a36Sopenharmony_ci int chargers_externally_control = 1; 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ci ret = sscanf(buf, "%d", &externally_control); 111862306a36Sopenharmony_ci if (ret == 0) { 111962306a36Sopenharmony_ci ret = -EINVAL; 112062306a36Sopenharmony_ci return ret; 112162306a36Sopenharmony_ci } 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci if (!externally_control) { 112462306a36Sopenharmony_ci charger->externally_control = 0; 112562306a36Sopenharmony_ci return count; 112662306a36Sopenharmony_ci } 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_ci for (i = 0; i < desc->num_charger_regulators; i++) { 112962306a36Sopenharmony_ci if (&desc->charger_regulators[i] != charger && 113062306a36Sopenharmony_ci !desc->charger_regulators[i].externally_control) { 113162306a36Sopenharmony_ci /* 113262306a36Sopenharmony_ci * At least, one charger is controlled by 113362306a36Sopenharmony_ci * charger-manager 113462306a36Sopenharmony_ci */ 113562306a36Sopenharmony_ci chargers_externally_control = 0; 113662306a36Sopenharmony_ci break; 113762306a36Sopenharmony_ci } 113862306a36Sopenharmony_ci } 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci if (!chargers_externally_control) { 114162306a36Sopenharmony_ci if (cm->charger_enabled) { 114262306a36Sopenharmony_ci try_charger_enable(charger->cm, false); 114362306a36Sopenharmony_ci charger->externally_control = externally_control; 114462306a36Sopenharmony_ci try_charger_enable(charger->cm, true); 114562306a36Sopenharmony_ci } else { 114662306a36Sopenharmony_ci charger->externally_control = externally_control; 114762306a36Sopenharmony_ci } 114862306a36Sopenharmony_ci } else { 114962306a36Sopenharmony_ci dev_warn(cm->dev, 115062306a36Sopenharmony_ci "'%s' regulator should be controlled in charger-manager because charger-manager must need at least one charger for charging\n", 115162306a36Sopenharmony_ci charger->regulator_name); 115262306a36Sopenharmony_ci } 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ci return count; 115562306a36Sopenharmony_ci} 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci/** 115862306a36Sopenharmony_ci * charger_manager_prepare_sysfs - Prepare sysfs entry for each charger 115962306a36Sopenharmony_ci * @cm: the Charger Manager representing the battery. 116062306a36Sopenharmony_ci * 116162306a36Sopenharmony_ci * This function add sysfs entry for charger(regulator) to control charger from 116262306a36Sopenharmony_ci * user-space. If some development board use one more chargers for charging 116362306a36Sopenharmony_ci * but only need one charger on specific case which is dependent on user 116462306a36Sopenharmony_ci * scenario or hardware restrictions, the user enter 1 or 0(zero) to '/sys/ 116562306a36Sopenharmony_ci * class/power_supply/battery/charger.[index]/externally_control'. For example, 116662306a36Sopenharmony_ci * if user enter 1 to 'sys/class/power_supply/battery/charger.[index]/ 116762306a36Sopenharmony_ci * externally_control, this charger isn't controlled from charger-manager and 116862306a36Sopenharmony_ci * always stay off state of regulator. 116962306a36Sopenharmony_ci */ 117062306a36Sopenharmony_cistatic int charger_manager_prepare_sysfs(struct charger_manager *cm) 117162306a36Sopenharmony_ci{ 117262306a36Sopenharmony_ci struct charger_desc *desc = cm->desc; 117362306a36Sopenharmony_ci struct charger_regulator *charger; 117462306a36Sopenharmony_ci int chargers_externally_control = 1; 117562306a36Sopenharmony_ci char *name; 117662306a36Sopenharmony_ci int i; 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_ci /* Create sysfs entry to control charger(regulator) */ 117962306a36Sopenharmony_ci for (i = 0; i < desc->num_charger_regulators; i++) { 118062306a36Sopenharmony_ci charger = &desc->charger_regulators[i]; 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_ci name = devm_kasprintf(cm->dev, GFP_KERNEL, "charger.%d", i); 118362306a36Sopenharmony_ci if (!name) 118462306a36Sopenharmony_ci return -ENOMEM; 118562306a36Sopenharmony_ci 118662306a36Sopenharmony_ci charger->attrs[0] = &charger->attr_name.attr; 118762306a36Sopenharmony_ci charger->attrs[1] = &charger->attr_state.attr; 118862306a36Sopenharmony_ci charger->attrs[2] = &charger->attr_externally_control.attr; 118962306a36Sopenharmony_ci charger->attrs[3] = NULL; 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_ci charger->attr_grp.name = name; 119262306a36Sopenharmony_ci charger->attr_grp.attrs = charger->attrs; 119362306a36Sopenharmony_ci desc->sysfs_groups[i] = &charger->attr_grp; 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci sysfs_attr_init(&charger->attr_name.attr); 119662306a36Sopenharmony_ci charger->attr_name.attr.name = "name"; 119762306a36Sopenharmony_ci charger->attr_name.attr.mode = 0444; 119862306a36Sopenharmony_ci charger->attr_name.show = charger_name_show; 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_ci sysfs_attr_init(&charger->attr_state.attr); 120162306a36Sopenharmony_ci charger->attr_state.attr.name = "state"; 120262306a36Sopenharmony_ci charger->attr_state.attr.mode = 0444; 120362306a36Sopenharmony_ci charger->attr_state.show = charger_state_show; 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci sysfs_attr_init(&charger->attr_externally_control.attr); 120662306a36Sopenharmony_ci charger->attr_externally_control.attr.name 120762306a36Sopenharmony_ci = "externally_control"; 120862306a36Sopenharmony_ci charger->attr_externally_control.attr.mode = 0644; 120962306a36Sopenharmony_ci charger->attr_externally_control.show 121062306a36Sopenharmony_ci = charger_externally_control_show; 121162306a36Sopenharmony_ci charger->attr_externally_control.store 121262306a36Sopenharmony_ci = charger_externally_control_store; 121362306a36Sopenharmony_ci 121462306a36Sopenharmony_ci if (!desc->charger_regulators[i].externally_control || 121562306a36Sopenharmony_ci !chargers_externally_control) 121662306a36Sopenharmony_ci chargers_externally_control = 0; 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_ci dev_info(cm->dev, "'%s' regulator's externally_control is %d\n", 121962306a36Sopenharmony_ci charger->regulator_name, charger->externally_control); 122062306a36Sopenharmony_ci } 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_ci if (chargers_externally_control) { 122362306a36Sopenharmony_ci dev_err(cm->dev, "Cannot register regulator because charger-manager must need at least one charger for charging battery\n"); 122462306a36Sopenharmony_ci return -EINVAL; 122562306a36Sopenharmony_ci } 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_ci return 0; 122862306a36Sopenharmony_ci} 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_cistatic int cm_init_thermal_data(struct charger_manager *cm, 123162306a36Sopenharmony_ci struct power_supply *fuel_gauge, 123262306a36Sopenharmony_ci enum power_supply_property *properties, 123362306a36Sopenharmony_ci size_t *num_properties) 123462306a36Sopenharmony_ci{ 123562306a36Sopenharmony_ci struct charger_desc *desc = cm->desc; 123662306a36Sopenharmony_ci union power_supply_propval val; 123762306a36Sopenharmony_ci int ret; 123862306a36Sopenharmony_ci 123962306a36Sopenharmony_ci /* Verify whether fuel gauge provides battery temperature */ 124062306a36Sopenharmony_ci ret = power_supply_get_property(fuel_gauge, 124162306a36Sopenharmony_ci POWER_SUPPLY_PROP_TEMP, &val); 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci if (!ret) { 124462306a36Sopenharmony_ci properties[*num_properties] = POWER_SUPPLY_PROP_TEMP; 124562306a36Sopenharmony_ci (*num_properties)++; 124662306a36Sopenharmony_ci cm->desc->measure_battery_temp = true; 124762306a36Sopenharmony_ci } 124862306a36Sopenharmony_ci#ifdef CONFIG_THERMAL 124962306a36Sopenharmony_ci if (ret && desc->thermal_zone) { 125062306a36Sopenharmony_ci cm->tzd_batt = 125162306a36Sopenharmony_ci thermal_zone_get_zone_by_name(desc->thermal_zone); 125262306a36Sopenharmony_ci if (IS_ERR(cm->tzd_batt)) 125362306a36Sopenharmony_ci return PTR_ERR(cm->tzd_batt); 125462306a36Sopenharmony_ci 125562306a36Sopenharmony_ci /* Use external thermometer */ 125662306a36Sopenharmony_ci properties[*num_properties] = POWER_SUPPLY_PROP_TEMP; 125762306a36Sopenharmony_ci (*num_properties)++; 125862306a36Sopenharmony_ci cm->desc->measure_battery_temp = true; 125962306a36Sopenharmony_ci ret = 0; 126062306a36Sopenharmony_ci } 126162306a36Sopenharmony_ci#endif 126262306a36Sopenharmony_ci if (cm->desc->measure_battery_temp) { 126362306a36Sopenharmony_ci /* NOTICE : Default allowable minimum charge temperature is 0 */ 126462306a36Sopenharmony_ci if (!desc->temp_max) 126562306a36Sopenharmony_ci desc->temp_max = CM_DEFAULT_CHARGE_TEMP_MAX; 126662306a36Sopenharmony_ci if (!desc->temp_diff) 126762306a36Sopenharmony_ci desc->temp_diff = CM_DEFAULT_RECHARGE_TEMP_DIFF; 126862306a36Sopenharmony_ci } 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ci return ret; 127162306a36Sopenharmony_ci} 127262306a36Sopenharmony_ci 127362306a36Sopenharmony_cistatic const struct of_device_id charger_manager_match[] = { 127462306a36Sopenharmony_ci { 127562306a36Sopenharmony_ci .compatible = "charger-manager", 127662306a36Sopenharmony_ci }, 127762306a36Sopenharmony_ci {}, 127862306a36Sopenharmony_ci}; 127962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, charger_manager_match); 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_cistatic struct charger_desc *of_cm_parse_desc(struct device *dev) 128262306a36Sopenharmony_ci{ 128362306a36Sopenharmony_ci struct charger_desc *desc; 128462306a36Sopenharmony_ci struct device_node *np = dev->of_node; 128562306a36Sopenharmony_ci u32 poll_mode = CM_POLL_DISABLE; 128662306a36Sopenharmony_ci u32 battery_stat = CM_NO_BATTERY; 128762306a36Sopenharmony_ci int num_chgs = 0; 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_ci desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL); 129062306a36Sopenharmony_ci if (!desc) 129162306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 129262306a36Sopenharmony_ci 129362306a36Sopenharmony_ci of_property_read_string(np, "cm-name", &desc->psy_name); 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_ci of_property_read_u32(np, "cm-poll-mode", &poll_mode); 129662306a36Sopenharmony_ci desc->polling_mode = poll_mode; 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_ci of_property_read_u32(np, "cm-poll-interval", 129962306a36Sopenharmony_ci &desc->polling_interval_ms); 130062306a36Sopenharmony_ci 130162306a36Sopenharmony_ci of_property_read_u32(np, "cm-fullbatt-vchkdrop-volt", 130262306a36Sopenharmony_ci &desc->fullbatt_vchkdrop_uV); 130362306a36Sopenharmony_ci of_property_read_u32(np, "cm-fullbatt-voltage", &desc->fullbatt_uV); 130462306a36Sopenharmony_ci of_property_read_u32(np, "cm-fullbatt-soc", &desc->fullbatt_soc); 130562306a36Sopenharmony_ci of_property_read_u32(np, "cm-fullbatt-capacity", 130662306a36Sopenharmony_ci &desc->fullbatt_full_capacity); 130762306a36Sopenharmony_ci 130862306a36Sopenharmony_ci of_property_read_u32(np, "cm-battery-stat", &battery_stat); 130962306a36Sopenharmony_ci desc->battery_present = battery_stat; 131062306a36Sopenharmony_ci 131162306a36Sopenharmony_ci /* chargers */ 131262306a36Sopenharmony_ci num_chgs = of_property_count_strings(np, "cm-chargers"); 131362306a36Sopenharmony_ci if (num_chgs > 0) { 131462306a36Sopenharmony_ci int i; 131562306a36Sopenharmony_ci 131662306a36Sopenharmony_ci /* Allocate empty bin at the tail of array */ 131762306a36Sopenharmony_ci desc->psy_charger_stat = devm_kcalloc(dev, 131862306a36Sopenharmony_ci num_chgs + 1, 131962306a36Sopenharmony_ci sizeof(char *), 132062306a36Sopenharmony_ci GFP_KERNEL); 132162306a36Sopenharmony_ci if (!desc->psy_charger_stat) 132262306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_ci for (i = 0; i < num_chgs; i++) 132562306a36Sopenharmony_ci of_property_read_string_index(np, "cm-chargers", 132662306a36Sopenharmony_ci i, &desc->psy_charger_stat[i]); 132762306a36Sopenharmony_ci } 132862306a36Sopenharmony_ci 132962306a36Sopenharmony_ci of_property_read_string(np, "cm-fuel-gauge", &desc->psy_fuel_gauge); 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_ci of_property_read_string(np, "cm-thermal-zone", &desc->thermal_zone); 133262306a36Sopenharmony_ci 133362306a36Sopenharmony_ci of_property_read_u32(np, "cm-battery-cold", &desc->temp_min); 133462306a36Sopenharmony_ci if (of_property_read_bool(np, "cm-battery-cold-in-minus")) 133562306a36Sopenharmony_ci desc->temp_min *= -1; 133662306a36Sopenharmony_ci of_property_read_u32(np, "cm-battery-hot", &desc->temp_max); 133762306a36Sopenharmony_ci of_property_read_u32(np, "cm-battery-temp-diff", &desc->temp_diff); 133862306a36Sopenharmony_ci 133962306a36Sopenharmony_ci of_property_read_u32(np, "cm-charging-max", 134062306a36Sopenharmony_ci &desc->charging_max_duration_ms); 134162306a36Sopenharmony_ci of_property_read_u32(np, "cm-discharging-max", 134262306a36Sopenharmony_ci &desc->discharging_max_duration_ms); 134362306a36Sopenharmony_ci 134462306a36Sopenharmony_ci /* battery charger regulators */ 134562306a36Sopenharmony_ci desc->num_charger_regulators = of_get_child_count(np); 134662306a36Sopenharmony_ci if (desc->num_charger_regulators) { 134762306a36Sopenharmony_ci struct charger_regulator *chg_regs; 134862306a36Sopenharmony_ci struct device_node *child; 134962306a36Sopenharmony_ci 135062306a36Sopenharmony_ci chg_regs = devm_kcalloc(dev, 135162306a36Sopenharmony_ci desc->num_charger_regulators, 135262306a36Sopenharmony_ci sizeof(*chg_regs), 135362306a36Sopenharmony_ci GFP_KERNEL); 135462306a36Sopenharmony_ci if (!chg_regs) 135562306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 135662306a36Sopenharmony_ci 135762306a36Sopenharmony_ci desc->charger_regulators = chg_regs; 135862306a36Sopenharmony_ci 135962306a36Sopenharmony_ci desc->sysfs_groups = devm_kcalloc(dev, 136062306a36Sopenharmony_ci desc->num_charger_regulators + 1, 136162306a36Sopenharmony_ci sizeof(*desc->sysfs_groups), 136262306a36Sopenharmony_ci GFP_KERNEL); 136362306a36Sopenharmony_ci if (!desc->sysfs_groups) 136462306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 136562306a36Sopenharmony_ci 136662306a36Sopenharmony_ci for_each_child_of_node(np, child) { 136762306a36Sopenharmony_ci struct charger_cable *cables; 136862306a36Sopenharmony_ci struct device_node *_child; 136962306a36Sopenharmony_ci 137062306a36Sopenharmony_ci of_property_read_string(child, "cm-regulator-name", 137162306a36Sopenharmony_ci &chg_regs->regulator_name); 137262306a36Sopenharmony_ci 137362306a36Sopenharmony_ci /* charger cables */ 137462306a36Sopenharmony_ci chg_regs->num_cables = of_get_child_count(child); 137562306a36Sopenharmony_ci if (chg_regs->num_cables) { 137662306a36Sopenharmony_ci cables = devm_kcalloc(dev, 137762306a36Sopenharmony_ci chg_regs->num_cables, 137862306a36Sopenharmony_ci sizeof(*cables), 137962306a36Sopenharmony_ci GFP_KERNEL); 138062306a36Sopenharmony_ci if (!cables) { 138162306a36Sopenharmony_ci of_node_put(child); 138262306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 138362306a36Sopenharmony_ci } 138462306a36Sopenharmony_ci 138562306a36Sopenharmony_ci chg_regs->cables = cables; 138662306a36Sopenharmony_ci 138762306a36Sopenharmony_ci for_each_child_of_node(child, _child) { 138862306a36Sopenharmony_ci of_property_read_string(_child, 138962306a36Sopenharmony_ci "cm-cable-name", &cables->name); 139062306a36Sopenharmony_ci of_property_read_string(_child, 139162306a36Sopenharmony_ci "cm-cable-extcon", 139262306a36Sopenharmony_ci &cables->extcon_name); 139362306a36Sopenharmony_ci of_property_read_u32(_child, 139462306a36Sopenharmony_ci "cm-cable-min", 139562306a36Sopenharmony_ci &cables->min_uA); 139662306a36Sopenharmony_ci of_property_read_u32(_child, 139762306a36Sopenharmony_ci "cm-cable-max", 139862306a36Sopenharmony_ci &cables->max_uA); 139962306a36Sopenharmony_ci cables++; 140062306a36Sopenharmony_ci } 140162306a36Sopenharmony_ci } 140262306a36Sopenharmony_ci chg_regs++; 140362306a36Sopenharmony_ci } 140462306a36Sopenharmony_ci } 140562306a36Sopenharmony_ci return desc; 140662306a36Sopenharmony_ci} 140762306a36Sopenharmony_ci 140862306a36Sopenharmony_cistatic inline struct charger_desc *cm_get_drv_data(struct platform_device *pdev) 140962306a36Sopenharmony_ci{ 141062306a36Sopenharmony_ci if (pdev->dev.of_node) 141162306a36Sopenharmony_ci return of_cm_parse_desc(&pdev->dev); 141262306a36Sopenharmony_ci return dev_get_platdata(&pdev->dev); 141362306a36Sopenharmony_ci} 141462306a36Sopenharmony_ci 141562306a36Sopenharmony_cistatic enum alarmtimer_restart cm_timer_func(struct alarm *alarm, ktime_t now) 141662306a36Sopenharmony_ci{ 141762306a36Sopenharmony_ci cm_timer_set = false; 141862306a36Sopenharmony_ci return ALARMTIMER_NORESTART; 141962306a36Sopenharmony_ci} 142062306a36Sopenharmony_ci 142162306a36Sopenharmony_cistatic int charger_manager_probe(struct platform_device *pdev) 142262306a36Sopenharmony_ci{ 142362306a36Sopenharmony_ci struct charger_desc *desc = cm_get_drv_data(pdev); 142462306a36Sopenharmony_ci struct charger_manager *cm; 142562306a36Sopenharmony_ci int ret, i = 0; 142662306a36Sopenharmony_ci union power_supply_propval val; 142762306a36Sopenharmony_ci struct power_supply *fuel_gauge; 142862306a36Sopenharmony_ci enum power_supply_property *properties; 142962306a36Sopenharmony_ci size_t num_properties; 143062306a36Sopenharmony_ci struct power_supply_config psy_cfg = {}; 143162306a36Sopenharmony_ci 143262306a36Sopenharmony_ci if (IS_ERR(desc)) { 143362306a36Sopenharmony_ci dev_err(&pdev->dev, "No platform data (desc) found\n"); 143462306a36Sopenharmony_ci return PTR_ERR(desc); 143562306a36Sopenharmony_ci } 143662306a36Sopenharmony_ci 143762306a36Sopenharmony_ci cm = devm_kzalloc(&pdev->dev, sizeof(*cm), GFP_KERNEL); 143862306a36Sopenharmony_ci if (!cm) 143962306a36Sopenharmony_ci return -ENOMEM; 144062306a36Sopenharmony_ci 144162306a36Sopenharmony_ci /* Basic Values. Unspecified are Null or 0 */ 144262306a36Sopenharmony_ci cm->dev = &pdev->dev; 144362306a36Sopenharmony_ci cm->desc = desc; 144462306a36Sopenharmony_ci psy_cfg.drv_data = cm; 144562306a36Sopenharmony_ci 144662306a36Sopenharmony_ci /* Initialize alarm timer */ 144762306a36Sopenharmony_ci if (alarmtimer_get_rtcdev()) { 144862306a36Sopenharmony_ci cm_timer = devm_kzalloc(cm->dev, sizeof(*cm_timer), GFP_KERNEL); 144962306a36Sopenharmony_ci if (!cm_timer) 145062306a36Sopenharmony_ci return -ENOMEM; 145162306a36Sopenharmony_ci alarm_init(cm_timer, ALARM_BOOTTIME, cm_timer_func); 145262306a36Sopenharmony_ci } 145362306a36Sopenharmony_ci 145462306a36Sopenharmony_ci /* 145562306a36Sopenharmony_ci * Some of the following do not need to be errors. 145662306a36Sopenharmony_ci * Users may intentionally ignore those features. 145762306a36Sopenharmony_ci */ 145862306a36Sopenharmony_ci if (desc->fullbatt_uV == 0) { 145962306a36Sopenharmony_ci dev_info(&pdev->dev, "Ignoring full-battery voltage threshold as it is not supplied\n"); 146062306a36Sopenharmony_ci } 146162306a36Sopenharmony_ci if (!desc->fullbatt_vchkdrop_uV) { 146262306a36Sopenharmony_ci dev_info(&pdev->dev, "Disabling full-battery voltage drop checking mechanism as it is not supplied\n"); 146362306a36Sopenharmony_ci desc->fullbatt_vchkdrop_uV = 0; 146462306a36Sopenharmony_ci } 146562306a36Sopenharmony_ci if (desc->fullbatt_soc == 0) { 146662306a36Sopenharmony_ci dev_info(&pdev->dev, "Ignoring full-battery soc(state of charge) threshold as it is not supplied\n"); 146762306a36Sopenharmony_ci } 146862306a36Sopenharmony_ci if (desc->fullbatt_full_capacity == 0) { 146962306a36Sopenharmony_ci dev_info(&pdev->dev, "Ignoring full-battery full capacity threshold as it is not supplied\n"); 147062306a36Sopenharmony_ci } 147162306a36Sopenharmony_ci 147262306a36Sopenharmony_ci if (!desc->charger_regulators || desc->num_charger_regulators < 1) { 147362306a36Sopenharmony_ci dev_err(&pdev->dev, "charger_regulators undefined\n"); 147462306a36Sopenharmony_ci return -EINVAL; 147562306a36Sopenharmony_ci } 147662306a36Sopenharmony_ci 147762306a36Sopenharmony_ci if (!desc->psy_charger_stat || !desc->psy_charger_stat[0]) { 147862306a36Sopenharmony_ci dev_err(&pdev->dev, "No power supply defined\n"); 147962306a36Sopenharmony_ci return -EINVAL; 148062306a36Sopenharmony_ci } 148162306a36Sopenharmony_ci 148262306a36Sopenharmony_ci if (!desc->psy_fuel_gauge) { 148362306a36Sopenharmony_ci dev_err(&pdev->dev, "No fuel gauge power supply defined\n"); 148462306a36Sopenharmony_ci return -EINVAL; 148562306a36Sopenharmony_ci } 148662306a36Sopenharmony_ci 148762306a36Sopenharmony_ci /* Check if charger's supplies are present at probe */ 148862306a36Sopenharmony_ci for (i = 0; desc->psy_charger_stat[i]; i++) { 148962306a36Sopenharmony_ci struct power_supply *psy; 149062306a36Sopenharmony_ci 149162306a36Sopenharmony_ci psy = power_supply_get_by_name(desc->psy_charger_stat[i]); 149262306a36Sopenharmony_ci if (!psy) { 149362306a36Sopenharmony_ci dev_err(&pdev->dev, "Cannot find power supply \"%s\"\n", 149462306a36Sopenharmony_ci desc->psy_charger_stat[i]); 149562306a36Sopenharmony_ci return -ENODEV; 149662306a36Sopenharmony_ci } 149762306a36Sopenharmony_ci power_supply_put(psy); 149862306a36Sopenharmony_ci } 149962306a36Sopenharmony_ci 150062306a36Sopenharmony_ci if (cm->desc->polling_mode != CM_POLL_DISABLE && 150162306a36Sopenharmony_ci (desc->polling_interval_ms == 0 || 150262306a36Sopenharmony_ci msecs_to_jiffies(desc->polling_interval_ms) <= CM_JIFFIES_SMALL)) { 150362306a36Sopenharmony_ci dev_err(&pdev->dev, "polling_interval_ms is too small\n"); 150462306a36Sopenharmony_ci return -EINVAL; 150562306a36Sopenharmony_ci } 150662306a36Sopenharmony_ci 150762306a36Sopenharmony_ci if (!desc->charging_max_duration_ms || 150862306a36Sopenharmony_ci !desc->discharging_max_duration_ms) { 150962306a36Sopenharmony_ci dev_info(&pdev->dev, "Cannot limit charging duration checking mechanism to prevent overcharge/overheat and control discharging duration\n"); 151062306a36Sopenharmony_ci desc->charging_max_duration_ms = 0; 151162306a36Sopenharmony_ci desc->discharging_max_duration_ms = 0; 151262306a36Sopenharmony_ci } 151362306a36Sopenharmony_ci 151462306a36Sopenharmony_ci platform_set_drvdata(pdev, cm); 151562306a36Sopenharmony_ci 151662306a36Sopenharmony_ci memcpy(&cm->charger_psy_desc, &psy_default, sizeof(psy_default)); 151762306a36Sopenharmony_ci 151862306a36Sopenharmony_ci if (!desc->psy_name) 151962306a36Sopenharmony_ci strncpy(cm->psy_name_buf, psy_default.name, PSY_NAME_MAX); 152062306a36Sopenharmony_ci else 152162306a36Sopenharmony_ci strncpy(cm->psy_name_buf, desc->psy_name, PSY_NAME_MAX); 152262306a36Sopenharmony_ci cm->charger_psy_desc.name = cm->psy_name_buf; 152362306a36Sopenharmony_ci 152462306a36Sopenharmony_ci /* Allocate for psy properties because they may vary */ 152562306a36Sopenharmony_ci properties = devm_kcalloc(&pdev->dev, 152662306a36Sopenharmony_ci ARRAY_SIZE(default_charger_props) + 152762306a36Sopenharmony_ci NUM_CHARGER_PSY_OPTIONAL, 152862306a36Sopenharmony_ci sizeof(*properties), GFP_KERNEL); 152962306a36Sopenharmony_ci if (!properties) 153062306a36Sopenharmony_ci return -ENOMEM; 153162306a36Sopenharmony_ci 153262306a36Sopenharmony_ci memcpy(properties, default_charger_props, 153362306a36Sopenharmony_ci sizeof(enum power_supply_property) * 153462306a36Sopenharmony_ci ARRAY_SIZE(default_charger_props)); 153562306a36Sopenharmony_ci num_properties = ARRAY_SIZE(default_charger_props); 153662306a36Sopenharmony_ci 153762306a36Sopenharmony_ci /* Find which optional psy-properties are available */ 153862306a36Sopenharmony_ci fuel_gauge = power_supply_get_by_name(desc->psy_fuel_gauge); 153962306a36Sopenharmony_ci if (!fuel_gauge) { 154062306a36Sopenharmony_ci dev_err(&pdev->dev, "Cannot find power supply \"%s\"\n", 154162306a36Sopenharmony_ci desc->psy_fuel_gauge); 154262306a36Sopenharmony_ci return -ENODEV; 154362306a36Sopenharmony_ci } 154462306a36Sopenharmony_ci if (!power_supply_get_property(fuel_gauge, 154562306a36Sopenharmony_ci POWER_SUPPLY_PROP_CHARGE_FULL, &val)) { 154662306a36Sopenharmony_ci properties[num_properties] = 154762306a36Sopenharmony_ci POWER_SUPPLY_PROP_CHARGE_FULL; 154862306a36Sopenharmony_ci num_properties++; 154962306a36Sopenharmony_ci } 155062306a36Sopenharmony_ci if (!power_supply_get_property(fuel_gauge, 155162306a36Sopenharmony_ci POWER_SUPPLY_PROP_CHARGE_NOW, &val)) { 155262306a36Sopenharmony_ci properties[num_properties] = 155362306a36Sopenharmony_ci POWER_SUPPLY_PROP_CHARGE_NOW; 155462306a36Sopenharmony_ci num_properties++; 155562306a36Sopenharmony_ci } 155662306a36Sopenharmony_ci if (!power_supply_get_property(fuel_gauge, 155762306a36Sopenharmony_ci POWER_SUPPLY_PROP_CURRENT_NOW, 155862306a36Sopenharmony_ci &val)) { 155962306a36Sopenharmony_ci properties[num_properties] = 156062306a36Sopenharmony_ci POWER_SUPPLY_PROP_CURRENT_NOW; 156162306a36Sopenharmony_ci num_properties++; 156262306a36Sopenharmony_ci } 156362306a36Sopenharmony_ci 156462306a36Sopenharmony_ci ret = cm_init_thermal_data(cm, fuel_gauge, properties, &num_properties); 156562306a36Sopenharmony_ci if (ret) { 156662306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to initialize thermal data\n"); 156762306a36Sopenharmony_ci cm->desc->measure_battery_temp = false; 156862306a36Sopenharmony_ci } 156962306a36Sopenharmony_ci power_supply_put(fuel_gauge); 157062306a36Sopenharmony_ci 157162306a36Sopenharmony_ci cm->charger_psy_desc.properties = properties; 157262306a36Sopenharmony_ci cm->charger_psy_desc.num_properties = num_properties; 157362306a36Sopenharmony_ci 157462306a36Sopenharmony_ci /* Register sysfs entry for charger(regulator) */ 157562306a36Sopenharmony_ci ret = charger_manager_prepare_sysfs(cm); 157662306a36Sopenharmony_ci if (ret < 0) { 157762306a36Sopenharmony_ci dev_err(&pdev->dev, 157862306a36Sopenharmony_ci "Cannot prepare sysfs entry of regulators\n"); 157962306a36Sopenharmony_ci return ret; 158062306a36Sopenharmony_ci } 158162306a36Sopenharmony_ci psy_cfg.attr_grp = desc->sysfs_groups; 158262306a36Sopenharmony_ci 158362306a36Sopenharmony_ci cm->charger_psy = power_supply_register(&pdev->dev, 158462306a36Sopenharmony_ci &cm->charger_psy_desc, 158562306a36Sopenharmony_ci &psy_cfg); 158662306a36Sopenharmony_ci if (IS_ERR(cm->charger_psy)) { 158762306a36Sopenharmony_ci dev_err(&pdev->dev, "Cannot register charger-manager with name \"%s\"\n", 158862306a36Sopenharmony_ci cm->charger_psy_desc.name); 158962306a36Sopenharmony_ci return PTR_ERR(cm->charger_psy); 159062306a36Sopenharmony_ci } 159162306a36Sopenharmony_ci 159262306a36Sopenharmony_ci /* Register extcon device for charger cable */ 159362306a36Sopenharmony_ci ret = charger_manager_register_extcon(cm); 159462306a36Sopenharmony_ci if (ret < 0) { 159562306a36Sopenharmony_ci dev_err(&pdev->dev, "Cannot initialize extcon device\n"); 159662306a36Sopenharmony_ci goto err_reg_extcon; 159762306a36Sopenharmony_ci } 159862306a36Sopenharmony_ci 159962306a36Sopenharmony_ci /* Add to the list */ 160062306a36Sopenharmony_ci mutex_lock(&cm_list_mtx); 160162306a36Sopenharmony_ci list_add(&cm->entry, &cm_list); 160262306a36Sopenharmony_ci mutex_unlock(&cm_list_mtx); 160362306a36Sopenharmony_ci 160462306a36Sopenharmony_ci /* 160562306a36Sopenharmony_ci * Charger-manager is capable of waking up the system from sleep 160662306a36Sopenharmony_ci * when event is happened through cm_notify_event() 160762306a36Sopenharmony_ci */ 160862306a36Sopenharmony_ci device_init_wakeup(&pdev->dev, true); 160962306a36Sopenharmony_ci device_set_wakeup_capable(&pdev->dev, false); 161062306a36Sopenharmony_ci 161162306a36Sopenharmony_ci /* 161262306a36Sopenharmony_ci * Charger-manager have to check the charging state right after 161362306a36Sopenharmony_ci * initialization of charger-manager and then update current charging 161462306a36Sopenharmony_ci * state. 161562306a36Sopenharmony_ci */ 161662306a36Sopenharmony_ci cm_monitor(); 161762306a36Sopenharmony_ci 161862306a36Sopenharmony_ci schedule_work(&setup_polling); 161962306a36Sopenharmony_ci 162062306a36Sopenharmony_ci return 0; 162162306a36Sopenharmony_ci 162262306a36Sopenharmony_cierr_reg_extcon: 162362306a36Sopenharmony_ci for (i = 0; i < desc->num_charger_regulators; i++) 162462306a36Sopenharmony_ci regulator_put(desc->charger_regulators[i].consumer); 162562306a36Sopenharmony_ci 162662306a36Sopenharmony_ci power_supply_unregister(cm->charger_psy); 162762306a36Sopenharmony_ci 162862306a36Sopenharmony_ci return ret; 162962306a36Sopenharmony_ci} 163062306a36Sopenharmony_ci 163162306a36Sopenharmony_cistatic int charger_manager_remove(struct platform_device *pdev) 163262306a36Sopenharmony_ci{ 163362306a36Sopenharmony_ci struct charger_manager *cm = platform_get_drvdata(pdev); 163462306a36Sopenharmony_ci struct charger_desc *desc = cm->desc; 163562306a36Sopenharmony_ci int i = 0; 163662306a36Sopenharmony_ci 163762306a36Sopenharmony_ci /* Remove from the list */ 163862306a36Sopenharmony_ci mutex_lock(&cm_list_mtx); 163962306a36Sopenharmony_ci list_del(&cm->entry); 164062306a36Sopenharmony_ci mutex_unlock(&cm_list_mtx); 164162306a36Sopenharmony_ci 164262306a36Sopenharmony_ci cancel_work_sync(&setup_polling); 164362306a36Sopenharmony_ci cancel_delayed_work_sync(&cm_monitor_work); 164462306a36Sopenharmony_ci 164562306a36Sopenharmony_ci for (i = 0 ; i < desc->num_charger_regulators ; i++) 164662306a36Sopenharmony_ci regulator_put(desc->charger_regulators[i].consumer); 164762306a36Sopenharmony_ci 164862306a36Sopenharmony_ci power_supply_unregister(cm->charger_psy); 164962306a36Sopenharmony_ci 165062306a36Sopenharmony_ci try_charger_enable(cm, false); 165162306a36Sopenharmony_ci 165262306a36Sopenharmony_ci return 0; 165362306a36Sopenharmony_ci} 165462306a36Sopenharmony_ci 165562306a36Sopenharmony_cistatic const struct platform_device_id charger_manager_id[] = { 165662306a36Sopenharmony_ci { "charger-manager", 0 }, 165762306a36Sopenharmony_ci { }, 165862306a36Sopenharmony_ci}; 165962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(platform, charger_manager_id); 166062306a36Sopenharmony_ci 166162306a36Sopenharmony_cistatic int cm_suspend_noirq(struct device *dev) 166262306a36Sopenharmony_ci{ 166362306a36Sopenharmony_ci if (device_may_wakeup(dev)) { 166462306a36Sopenharmony_ci device_set_wakeup_capable(dev, false); 166562306a36Sopenharmony_ci return -EAGAIN; 166662306a36Sopenharmony_ci } 166762306a36Sopenharmony_ci 166862306a36Sopenharmony_ci return 0; 166962306a36Sopenharmony_ci} 167062306a36Sopenharmony_ci 167162306a36Sopenharmony_cistatic bool cm_need_to_awake(void) 167262306a36Sopenharmony_ci{ 167362306a36Sopenharmony_ci struct charger_manager *cm; 167462306a36Sopenharmony_ci 167562306a36Sopenharmony_ci if (cm_timer) 167662306a36Sopenharmony_ci return false; 167762306a36Sopenharmony_ci 167862306a36Sopenharmony_ci mutex_lock(&cm_list_mtx); 167962306a36Sopenharmony_ci list_for_each_entry(cm, &cm_list, entry) { 168062306a36Sopenharmony_ci if (is_charging(cm)) { 168162306a36Sopenharmony_ci mutex_unlock(&cm_list_mtx); 168262306a36Sopenharmony_ci return true; 168362306a36Sopenharmony_ci } 168462306a36Sopenharmony_ci } 168562306a36Sopenharmony_ci mutex_unlock(&cm_list_mtx); 168662306a36Sopenharmony_ci 168762306a36Sopenharmony_ci return false; 168862306a36Sopenharmony_ci} 168962306a36Sopenharmony_ci 169062306a36Sopenharmony_cistatic int cm_suspend_prepare(struct device *dev) 169162306a36Sopenharmony_ci{ 169262306a36Sopenharmony_ci if (cm_need_to_awake()) 169362306a36Sopenharmony_ci return -EBUSY; 169462306a36Sopenharmony_ci 169562306a36Sopenharmony_ci if (!cm_suspended) 169662306a36Sopenharmony_ci cm_suspended = true; 169762306a36Sopenharmony_ci 169862306a36Sopenharmony_ci cm_timer_set = cm_setup_timer(); 169962306a36Sopenharmony_ci 170062306a36Sopenharmony_ci if (cm_timer_set) { 170162306a36Sopenharmony_ci cancel_work_sync(&setup_polling); 170262306a36Sopenharmony_ci cancel_delayed_work_sync(&cm_monitor_work); 170362306a36Sopenharmony_ci } 170462306a36Sopenharmony_ci 170562306a36Sopenharmony_ci return 0; 170662306a36Sopenharmony_ci} 170762306a36Sopenharmony_ci 170862306a36Sopenharmony_cistatic void cm_suspend_complete(struct device *dev) 170962306a36Sopenharmony_ci{ 171062306a36Sopenharmony_ci struct charger_manager *cm = dev_get_drvdata(dev); 171162306a36Sopenharmony_ci 171262306a36Sopenharmony_ci if (cm_suspended) 171362306a36Sopenharmony_ci cm_suspended = false; 171462306a36Sopenharmony_ci 171562306a36Sopenharmony_ci if (cm_timer_set) { 171662306a36Sopenharmony_ci ktime_t remain; 171762306a36Sopenharmony_ci 171862306a36Sopenharmony_ci alarm_cancel(cm_timer); 171962306a36Sopenharmony_ci cm_timer_set = false; 172062306a36Sopenharmony_ci remain = alarm_expires_remaining(cm_timer); 172162306a36Sopenharmony_ci cm_suspend_duration_ms -= ktime_to_ms(remain); 172262306a36Sopenharmony_ci schedule_work(&setup_polling); 172362306a36Sopenharmony_ci } 172462306a36Sopenharmony_ci 172562306a36Sopenharmony_ci _cm_monitor(cm); 172662306a36Sopenharmony_ci 172762306a36Sopenharmony_ci device_set_wakeup_capable(cm->dev, false); 172862306a36Sopenharmony_ci} 172962306a36Sopenharmony_ci 173062306a36Sopenharmony_cistatic const struct dev_pm_ops charger_manager_pm = { 173162306a36Sopenharmony_ci .prepare = cm_suspend_prepare, 173262306a36Sopenharmony_ci .suspend_noirq = cm_suspend_noirq, 173362306a36Sopenharmony_ci .complete = cm_suspend_complete, 173462306a36Sopenharmony_ci}; 173562306a36Sopenharmony_ci 173662306a36Sopenharmony_cistatic struct platform_driver charger_manager_driver = { 173762306a36Sopenharmony_ci .driver = { 173862306a36Sopenharmony_ci .name = "charger-manager", 173962306a36Sopenharmony_ci .pm = &charger_manager_pm, 174062306a36Sopenharmony_ci .of_match_table = charger_manager_match, 174162306a36Sopenharmony_ci }, 174262306a36Sopenharmony_ci .probe = charger_manager_probe, 174362306a36Sopenharmony_ci .remove = charger_manager_remove, 174462306a36Sopenharmony_ci .id_table = charger_manager_id, 174562306a36Sopenharmony_ci}; 174662306a36Sopenharmony_ci 174762306a36Sopenharmony_cistatic int __init charger_manager_init(void) 174862306a36Sopenharmony_ci{ 174962306a36Sopenharmony_ci cm_wq = create_freezable_workqueue("charger_manager"); 175062306a36Sopenharmony_ci if (unlikely(!cm_wq)) 175162306a36Sopenharmony_ci return -ENOMEM; 175262306a36Sopenharmony_ci 175362306a36Sopenharmony_ci INIT_DELAYED_WORK(&cm_monitor_work, cm_monitor_poller); 175462306a36Sopenharmony_ci 175562306a36Sopenharmony_ci return platform_driver_register(&charger_manager_driver); 175662306a36Sopenharmony_ci} 175762306a36Sopenharmony_cilate_initcall(charger_manager_init); 175862306a36Sopenharmony_ci 175962306a36Sopenharmony_cistatic void __exit charger_manager_cleanup(void) 176062306a36Sopenharmony_ci{ 176162306a36Sopenharmony_ci destroy_workqueue(cm_wq); 176262306a36Sopenharmony_ci cm_wq = NULL; 176362306a36Sopenharmony_ci 176462306a36Sopenharmony_ci platform_driver_unregister(&charger_manager_driver); 176562306a36Sopenharmony_ci} 176662306a36Sopenharmony_cimodule_exit(charger_manager_cleanup); 176762306a36Sopenharmony_ci 176862306a36Sopenharmony_ciMODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>"); 176962306a36Sopenharmony_ciMODULE_DESCRIPTION("Charger Manager"); 177062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 1771